Index: src/plugin/PluginFilter.cpp
===================================================================
--- a/src/plugin/PluginFilter.cpp
+++ b/src/plugin/PluginFilter.cpp
@@ -23,6 +23,7 @@
 #include "PluginSettings.h"
 #include "PluginSystem.h"
 #include "PluginClass.h"
+#include "PluginUtil.h"
 #include "mlang.h"
 #include "..\shared\CriticalSection.h"
 #include "..\shared\Utils.h"
@@ -33,176 +34,177 @@
 static CriticalSection s_criticalSectionFilterMap;
 
 // ============================================================================
-// CFilterElementHideAttrSelector
+// CFilterElementHide
 // ============================================================================
-
-CFilterElementHideAttrSelector::CFilterElementHideAttrSelector() : m_type(TYPE_NONE), m_pos(POS_NONE), m_bstrAttr(NULL)
+namespace
 {
+  class ElementHideParseException
+    : public std::runtime_error
+  {
+    std::string message(const std::wstring& filterText, const std::string& reason)
+    {
+      std::string s(
+        "CFilterElementHide::CFilterElementHide, error parsing selector \""
+        + ToUtf8String(filterText) + "\" (" + reason + ")"
+        );
+      DEBUG_FILTER(ToUtf16String(s));
+      return s;
+    }
+  public:
+    ElementHideParseException(const std::wstring& filterText, const std::string& reason)
+      : std::runtime_error(message(filterText, reason))
+    {}
+  };
 }
 
-CFilterElementHideAttrSelector::CFilterElementHideAttrSelector(const CFilterElementHideAttrSelector& filter)
+CFilterElementHide::CFilterElementHide(const std::wstring& filterText)
+  : m_filterText(filterText), m_type(ETraverserComplexType::TRAVERSER_TYPE_ERROR)
 {
-  m_type = filter.m_type;
-  m_pos = filter.m_pos;
-  m_bstrAttr = filter.m_bstrAttr;
+  std::wstring filterSuffix(filterText); // The unparsed remainder of the input filter text
 
-  m_value = filter.m_value;
-}
-
-CFilterElementHideAttrSelector::~CFilterElementHideAttrSelector()
-{
-}
-
-
-// ============================================================================
-// CFilterElementHide
-// ============================================================================
-
-CFilterElementHide::CFilterElementHide(const CString& filterText) : m_filterText(filterText), m_type(ETraverserComplexType::TRAVERSER_TYPE_ERROR)
-{
   // Find tag name, class or any (*)
-  CString filterString = filterText;
-
-  wchar_t firstTag = filterString.GetAt(0);
-  // Any tag
+  wchar_t firstTag = filterText[0];
   if (firstTag == '*')
   {
-    filterString = filterString.Mid(1);
+    // Any tag
+    filterSuffix = filterSuffix.substr(1);
   }
-  // Any tag (implicitely)
   else if (firstTag == '[' || firstTag == '.' || firstTag == '#')
   {
+    // Any tag (implicitly)
   }
-  // Real tag
   else if (isalnum(firstTag))
   {
-    //TODO: Add support for descendant selectors
-    int pos = filterString.FindOneOf(L".#[(");
-
-    if (pos < 0)
-      pos = filterString.GetLength();
-    m_tag = filterString.Left(pos).MakeLower();
-
-    filterString = filterString.Mid(pos);
+    // Real tag
+    // TODO: Add support for descendant selectors
+    auto pos = filterSuffix.find_first_of(L".#[(");
+    if (pos == std::wstring::npos)
+    {
+      pos = filterSuffix.length();
+    }
+    m_tag = ToLowerString(filterSuffix.substr(0, pos));
+    filterSuffix = filterSuffix.substr(pos);
   }
-  // Error
   else
   {
-    DEBUG_FILTER("Filter::Error parsing selector:" + filterText + " (invalid tag)");
-    throw std::runtime_error(CStringA("Filter::Error parsing selector:" + filterText + " (invalid tag)").GetString());
+    // Error
+    throw ElementHideParseException(filterText, "invalid tag");
   }
 
   // Find Id and class name
-
-  if (!filterString.IsEmpty())
+  if (!filterSuffix.empty())
   {
-    wchar_t firstId = filterString.GetAt(0);
+    wchar_t firstId = filterSuffix[0];
 
     // Id
     if (firstId == '#')
     {
-      int pos = filterString.Find('[');
-      if (pos < 0)
+      auto pos = filterSuffix.find('[');
+      if (pos == std::wstring::npos)
       {
-        pos = filterString.GetLength();
+        pos = filterSuffix.length();
       }
-      m_tagId = filterString.Mid(1, pos - 1);
-      filterString = filterString.Mid(pos);
-      pos = m_tagId.Find(L".");
-      if (pos > 0)
+      m_tagId = filterSuffix.substr(1, pos - 1);
+      filterSuffix = filterSuffix.substr(pos);
+      pos = m_tagId.find(L".");
+      if (pos != std::wstring::npos)
       {
-        m_tagClassName = m_tagId.Mid(pos + 1);
-        m_tagId = m_tagId.Left(pos);
+        if (pos == 0)
+        {
+          throw ElementHideParseException(filterText, "empty tag id");
+        }
+        m_tagClassName = m_tagId.substr(pos + 1);
+        m_tagId = m_tagId.substr(0, pos);
       }
     }
     // Class name
     else if (firstId == '.')
     {
-      int pos = filterString.Find('[');
-      if (pos < 0)
+      auto pos = filterSuffix.find('[');
+      if (pos == std::wstring::npos)
       {
-        pos = filterString.GetLength();
+        pos = filterSuffix.length();
       }
-      m_tagClassName = filterString.Mid(1, pos - 1);
-      filterString = filterString.Mid(pos);
+      m_tagClassName = filterSuffix.substr(1, pos - 1);
+      filterSuffix = filterSuffix.substr(pos);
     }
   }
 
-  char chAttrStart = '[';
-  char chAttrEnd   = ']';
+  while (!filterSuffix.empty())
+  {
+    if (filterSuffix[0] != '[')
+    {
+      throw ElementHideParseException(filterText, "expected '['");
+    }
+    auto endPos = filterSuffix.find(']') ;
+    if (endPos == std::wstring::npos)
+    {
+      throw ElementHideParseException(filterText, "expected ']'");
+    }
+    std::wstring arg = filterSuffix.substr(1, endPos - 1);
+    filterSuffix = filterSuffix.substr(endPos + 1);
 
-  while (!filterString.IsEmpty())
-  {
-    if (filterString.GetAt(0) != chAttrStart)
+    CFilterElementHideAttrSelector attrSelector;
+    auto posEquals = arg.find('=');
+    if (posEquals != std::wstring::npos)
     {
-      DEBUG_FILTER("Filter::Error parsing selector:" + filterText + " (more data)");
-      throw std::runtime_error(CStringA("Filter::Error parsing selector:" + filterText + " (more data)").GetString());
-    }
-    int endPos = filterString.Find(']') ;
-    if (endPos < 0)
-    {
-      DEBUG_FILTER("Filter::Error parsing selector:" + filterText + " (more data)");
-      throw std::runtime_error(CStringA("Filter::Error parsing selector:" + filterText + " (more data)").GetString());
-    }
-        
-    CFilterElementHideAttrSelector attrSelector;
-
-    CString arg = filterString.Mid(1, endPos - 1);
-    filterString = filterString.Mid(endPos + 1);
-
-    int delimiterPos = arg.Find('=');
-    if (delimiterPos > 0)
-    {
-      attrSelector.m_value = arg.Mid(delimiterPos + 1);
-      if (attrSelector.m_value.GetLength() >= 2 && attrSelector.m_value.GetAt(0) == '\"' && attrSelector.m_value.GetAt(attrSelector.m_value.GetLength() - 1) == '\"')
+      if (posEquals == 0)
       {
-        attrSelector.m_value = attrSelector.m_value.Mid(1, attrSelector.m_value.GetLength() - 2);
+        throw ElementHideParseException(filterText, "empty attribute name before '='");
+      }
+      attrSelector.m_value = arg.substr(posEquals + 1);
+      if (attrSelector.m_value.length() >= 2 && attrSelector.m_value[0] == '\"' && attrSelector.m_value[attrSelector.m_value.length() - 1] == '\"')
+      {
+        attrSelector.m_value = attrSelector.m_value.substr(1, attrSelector.m_value.length() - 2);
       }
 
-      if (arg.GetAt(delimiterPos - 1) == '^')
+      if (arg[posEquals - 1] == '^')
       {
-        attrSelector.m_bstrAttr = arg.Left(delimiterPos - 1);
         attrSelector.m_pos = CFilterElementHideAttrPos::STARTING;
       }
-      else if (arg.GetAt(delimiterPos - 1) == '*')
+      else if (arg[posEquals - 1] == '*')
       {
-        attrSelector.m_bstrAttr = arg.Left(delimiterPos - 1);
         attrSelector.m_pos = CFilterElementHideAttrPos::ANYWHERE;
       }
-      else if (arg.GetAt(delimiterPos - 1) == '$')
+      else if (arg[posEquals - 1] == '$')
       {
-        attrSelector.m_bstrAttr = arg.Left(delimiterPos - 1);
         attrSelector.m_pos = CFilterElementHideAttrPos::ENDING;
       }
+      if (attrSelector.m_pos != CFilterElementHideAttrPos::POS_NONE)
+      {
+        if (posEquals == 1)
+        {
+          throw ElementHideParseException(filterText, "empty attribute name before " + std::string(1, static_cast<char>(arg[0])) + "'='");
+        }
+        attrSelector.m_attr = arg.substr(0, posEquals - 1);
+      }
       else
       {
-        attrSelector.m_bstrAttr = arg.Left(delimiterPos);
         attrSelector.m_pos = CFilterElementHideAttrPos::EXACT;
+        attrSelector.m_attr = arg.substr(0, posEquals);
       }
     }
-    CString tag = attrSelector.m_bstrAttr;
-    if (tag == "style")
+
+    if (attrSelector.m_attr == L"style")
     {
       attrSelector.m_type = CFilterElementHideAttrType::STYLE;
-      attrSelector.m_value.MakeLower();
+      attrSelector.m_value = ToLowerString(attrSelector.m_value);
     }
-    else if (tag == "id")
+    else if (attrSelector.m_attr == L"id")
     {
       attrSelector.m_type = CFilterElementHideAttrType::ID;
     }
-    else if (tag == "class")
+    else if (attrSelector.m_attr == L"class")
     {
       attrSelector.m_type = CFilterElementHideAttrType::CLASS;
     }
     m_attributeSelectors.push_back(attrSelector);
-
   }
 
   // End check
-  if (!filterString.IsEmpty())
+  if (!filterSuffix.empty())
   {
-    DEBUG_FILTER("Filter::Error parsing selector:" + filterFile + "/" + filterText + " (more data)")
-    throw new std::runtime_error(CStringA("Filter::Error parsing selector:"  + filterText + " (more data)").GetString());
+    throw ElementHideParseException(filterText, "extra characters at end");
   }
 }
 
@@ -250,96 +252,115 @@
 bool CFilterElementHide::IsMatchFilterElementHide(IHTMLElement* pEl) const
 {
   HRESULT hr;
-
-  if (!m_tagId.IsEmpty())
+  /*
+   * If a tag id is specified, it must match
+   */
+  if (!m_tagId.empty())
   {
-    CComBSTR id;
-    hr = pEl->get_id(&id);
-    if ((hr != S_OK) || (id != CComBSTR(m_tagId)))
+    CComBSTR idBstr;
+    if (FAILED(pEl->get_id(&idBstr)) || !idBstr || m_tagId != ToWstring(idBstr))
     {
       return false;
     }
   }
-  if (!m_tagClassName.IsEmpty())
+  /*
+   * If a class name is specified, it must match
+   */
+  if (!m_tagClassName.empty())
   {
-    CComBSTR classNameBSTR;
-    hr = pEl->get_className(&classNameBSTR);
-    if (hr == S_OK)
+    CComBSTR classNameListBstr;
+    hr = pEl->get_className(&classNameListBstr);
+    if (FAILED(hr) || !classNameListBstr)
     {
-      CString className = classNameBSTR;
-      int start = 0;
-      CString specificClass;
-      bool foundMatch = false;
-      while ((specificClass = className.Tokenize(L" ", start)) != L"")
+      return false; // We can't match a class name if there's no class name
+    }
+    std::wstring classNameList(ToWstring(classNameListBstr));
+    if (classNameList.empty())
+    {
+      return false;
+    }
+    // TODO: Consider case of multiple classes. (m_tagClassName can be something like "foo.bar")
+    /*
+     * Match when 'm_tagClassName' appears as a token within classNameList
+     */
+    bool foundMatch = false;
+    wchar_t* nextToken = nullptr;
+    const wchar_t* token = wcstok_s(&classNameList[0], L" ", &nextToken);
+    while (token != nullptr)
+    {
+      if (std::wstring(token) == m_tagClassName)
       {
-        // TODO: Consider case of multiple classes. (m_tagClassName can be something like "foo.bar")
-        if (specificClass == m_tagClassName)
-        {
-          foundMatch = true;
-        }
+        foundMatch = true;
+        break;
       }
-      if (!foundMatch)
-      {
-        return false;
-      }
+      token = wcstok_s(nullptr, L" ", &nextToken);
     }
-  }
-  if (!m_tag.IsEmpty())
-  {
-    CComBSTR tagName;
-    hr = pEl->get_tagName(&tagName);
-    tagName.ToLower();
-    if ((hr != S_OK) || (tagName != CComBSTR(m_tag)))
+    if (!foundMatch)
     {
       return false;
     }
   }
-
-  // Check attributes
-  for (std::vector<CFilterElementHideAttrSelector>::const_iterator attrIt = m_attributeSelectors.begin(); 
-        attrIt != m_attributeSelectors.end(); ++ attrIt)
+  /*
+   * If a tag name is specified, it must match
+   */
+  if (!m_tag.empty())
   {
-    ATL::CString value;
+    CComBSTR tagNameBstr;
+    if (FAILED(pEl->get_tagName(&tagNameBstr)) || !tagNameBstr)
+    {
+      return false;
+    }
+    if (m_tag != ToLowerString(ToWstring(tagNameBstr)))
+    {
+      return false;
+    }
+  }
+  /*
+   * Match each attribute
+   */
+  for (auto attrIt = m_attributeSelectors.begin(); attrIt != m_attributeSelectors.end(); ++attrIt)
+  {
+    std::wstring value;
     bool attrFound = false;
     if (attrIt->m_type == CFilterElementHideAttrType::STYLE)
     {
       CComPtr<IHTMLStyle> pStyle;
       if (SUCCEEDED(pEl->get_style(&pStyle)) && pStyle)
       {
-        CComBSTR bstrStyle;
-
-        if (SUCCEEDED(pStyle->get_cssText(&bstrStyle)) && bstrStyle)
+        CComBSTR styleBstr;
+        if (SUCCEEDED(pStyle->get_cssText(&styleBstr)) && styleBstr)
         {
-          value = bstrStyle;
-          value.MakeLower();
+          value = ToLowerString(ToWstring(styleBstr));
           attrFound = true;
         }
       }
     }
     else if (attrIt->m_type == CFilterElementHideAttrType::CLASS)
     {
-      CComBSTR bstrClassNames;
-      if (SUCCEEDED(pEl->get_className(&bstrClassNames)) && bstrClassNames)
+      CComBSTR classNamesBstr;
+      if (SUCCEEDED(pEl->get_className(&classNamesBstr)) && classNamesBstr)
       {
-        value = bstrClassNames;
+        value = ToWstring(classNamesBstr);
         attrFound = true;
       }
     }
     else if (attrIt->m_type == CFilterElementHideAttrType::ID)
     {
-      CComBSTR bstrId;
-      if (SUCCEEDED(pEl->get_id(&bstrId)) && bstrId)
+      CComBSTR idBstr;
+      if (SUCCEEDED(pEl->get_id(&idBstr)) && idBstr)
       {
-        value = bstrId;
+        value = ToWstring(idBstr);
         attrFound = true;
       }
     }
     else
     {
-      auto attributeValue = GetHtmlElementAttribute(*pEl, attrIt->m_bstrAttr);
-      if (attrFound = attributeValue.isAttributeFound)
+      CComBSTR attrArgument(attrIt->m_attr.length(), attrIt->m_attr.c_str());
+      auto x = GetHtmlElementAttribute(*pEl, attrArgument);
+      attrFound = x.isAttributeFound;
+      if (attrFound)
       {
-        value = ToCString(attributeValue.attributeValue);
+        value = x.attributeValue;
       }
     }
 
@@ -353,20 +374,24 @@
       }
       else if (attrIt->m_pos == CFilterElementHideAttrPos::STARTING)
       {
-        if (value.Left(attrIt->m_value.GetLength()) != attrIt->m_value)
+        if (value.compare(0, attrIt->m_value.length(), attrIt->m_value) != 0)
           return false;
       }
       else if (attrIt->m_pos == CFilterElementHideAttrPos::ENDING)
       {
-        if (value.Right(attrIt->m_value.GetLength()) != attrIt->m_value)
+        size_t valueLength = value.length();
+        size_t attrLength = attrIt->m_value.length();
+        if (valueLength < attrLength)
+          return false;
+        if (value.compare(valueLength - attrLength, attrLength, attrIt->m_value) != 0)
           return false;
       }
       else if (attrIt->m_pos == CFilterElementHideAttrPos::ANYWHERE)
       {
-        if (value.Find(attrIt->m_value) < 0)
+        if (value.find(attrIt->m_value) == std::wstring::npos)
           return false;
       }
-      else if (attrIt->m_value.IsEmpty())
+      else if (attrIt->m_value.empty())
       {
         return true;
       }
@@ -423,41 +448,36 @@
 // CPluginFilter
 // ============================================================================
 
-CPluginFilter::CPluginFilter(const CString& dataPath) : m_dataPath(dataPath)
+CPluginFilter::CPluginFilter(const std::wstring& dataPath)
+  : m_dataPath(dataPath)
 {
   ClearFilters();
 }
 
 
-bool CPluginFilter::AddFilterElementHide(CString filterText)
+bool CPluginFilter::AddFilterElementHide(std::wstring filterText)
 {
-  DEBUG_FILTER("Input: " + filterText + " filterFile" + filterFile);
+  DEBUG_FILTER(L"Input: " + filterText + L" 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)
+      auto chunkEnd = filterText.find_first_of(L"+>");
+      if (chunkEnd != std::wstring::npos && chunkEnd > 0)
       {
-        separatorChar = filterText.GetAt(chunkEnd);
+        separatorChar = filterText[chunkEnd];
       }
       else
       {
-        chunkEnd = filterText.GetLength();
+        chunkEnd = filterText.length();
         separatorChar = L'\0';
       }
 
-      CString filterChunk = filterText.Left(chunkEnd).TrimRight();
       std::auto_ptr<CFilterElementHide> filterParent(filter);
-
-      filter.reset(new CFilterElementHide(filterChunk));
-
+      filter.reset(new CFilterElementHide(TrimStringRight(filterText.substr(0, chunkEnd))));
       if (filterParent.get() != 0)
       {
         filter->m_predecessor.reset(filterParent.release());
@@ -465,7 +485,7 @@
 
       if (separatorChar != L'\0') // complex selector
       {
-        filterText = filterText.Mid(chunkEnd + 1).TrimLeft();
+        filterText = TrimStringLeft(filterText.substr(chunkEnd + 1));
         if (separatorChar == '+')
           filter->m_type = CFilterElementHide::TRAVERSER_TYPE_IMMEDIATE;
         else if (separatorChar == '>')
@@ -473,18 +493,17 @@
       }
       else // Terminating element (simple selector)
       {
-        if (!filter->m_tagId.IsEmpty())
+        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.IsEmpty())
+        else if (!filter->m_tagClassName.empty())
         {
           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);
+          m_elementHideTags.insert(std::make_pair(filter->m_tag, *filter));
         }
       }
     } while (separatorChar != '\0');
@@ -495,50 +514,46 @@
 
 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)
+  std::wstring id;
+  CComBSTR idBstr;
+  if (SUCCEEDED(pEl->get_id(&idBstr)) && idBstr)
   {
-    id = bstrId;
+    id = ToWstring(idBstr);
   }
-
-  CString classNames;
-  CComBSTR bstrClassNames;
-  if (SUCCEEDED(pEl->get_className(&bstrClassNames)) && bstrClassNames)
+  std::wstring classNames;
+  CComBSTR classNamesBstr;
+  if (SUCCEEDED(pEl->get_className(&classNamesBstr)) && classNamesBstr)
   {
-    classNames = bstrClassNames;
+    classNames = ToWstring(classNamesBstr);
   }
 
   CriticalSection::Lock filterEngineLock(s_criticalSectionFilterMap);
   {
     // Search tag/id filters
-    if (!id.IsEmpty())
+    if (!id.empty())
     {
-      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 ++)
+      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 + "HideEl::Found (tag/id) filter:" + idIt->second.m_filterText)
-            CPluginDebug::DebugResultHiding(tag, L"id:" + ToWstring(id), ToWstring(idIt->second.m_filterText));
+          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("", id));
-      for (TFilterElementHideTagsNamed::const_iterator idIt = idItEnum.first; idIt != idItEnum.second; idIt ++)
+      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 + "HideEl::Found (?/id) filter:" + idIt->second.m_filterText)
-            CPluginDebug::DebugResultHiding(tag, L"id:" + ToWstring(id), ToWstring(idIt->second.m_filterText));
+          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;
         }
@@ -546,56 +561,52 @@
     }
 
     // Search tag/className filters
-    if (!classNames.IsEmpty())
+    if (!classNames.empty())
     {
-      int pos = 0;
-      CString className = classNames.Tokenize(L" \t\n\r", pos);
-      while (pos >= 0)
+      wchar_t* nextToken = nullptr;
+      const wchar_t* token = wcstok_s(&classNames[0], L" \t\n\r", &nextToken);
+      while (token != nullptr)
       {
-        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)
+        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 + "HideEl::Found (tag/class) filter:" + classIt->second.m_filterText)
-              CPluginDebug::DebugResultHiding(tag, L"class:" + ToWstring(className), ToWstring(classIt->second.m_filterText));
+            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("", className));
-        for (TFilterElementHideTagsNamed::const_iterator classIt = classItEnum.first; classIt != classItEnum.second; ++ classIt)
+        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:" + ToWString(classIt->second.m_filterText));
-            CPluginDebug::DebugResultHiding(tag, L"class:" + ToWstring(className), ToWstring(classIt->second.m_filterText));
+            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;
           }
         }
-
-        // Next class name
-        className = classNames.Tokenize(L" \t\n\r", pos);
+        token = wcstok_s(nullptr, L" \t\n\r", &nextToken);
       }
     }
 
     // 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)
+    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 + "HideEl::Found (tag) filter:" + tagIt->second.m_filterText)
-          CPluginDebug::DebugResultHiding(tag, L"-", ToWstring(tagIt->second.m_filterText));
+        DEBUG_HIDE_EL(indent + L"HideEl::Found (tag) filter:" + tagIt->second.m_filterText);
+        CPluginDebug::DebugResultHiding(tag, L"-", tagIt->second.m_filterText);
 #endif
         return true;
       }
@@ -615,11 +626,11 @@
   int pos = 0;
   CriticalSection::Lock filterEngineLock(s_criticalSectionFilterMap);
   {
-    for (std::vector<std::wstring>::iterator it = filters.begin(); it < filters.end(); ++it)
+    for (auto it = filters.begin(); it < filters.end(); ++it)
     {
-      CString filter((*it).c_str());
+      std::wstring filter(TrimString(*it));
       // If the line is not commented out
-      if (!filter.Trim().IsEmpty() && filter.GetAt(0) != '!' && filter.GetAt(0) != '[')
+      if (!filter.empty() && filter[0] != '!' && filter[0] != '[')
       {
         int filterType = 0;
 
@@ -632,7 +643,7 @@
         catch(...)
         {
 #ifdef ENABLE_DEBUG_RESULT
-          CPluginDebug::DebugResult(L"Error loading hide filter: " + ToWstring(filter));
+          CPluginDebug::DebugResult(L"Error loading hide filter: " + filter);
 #endif
         }
       }
Index: src/plugin/PluginFilter.h
===================================================================
--- a/src/plugin/PluginFilter.h
+++ b/src/plugin/PluginFilter.h
@@ -34,21 +34,17 @@
 // CFilterElementHideAttrSelector
 // ============================================================================
 
-class CFilterElementHideAttrSelector
+struct CFilterElementHideAttrSelector
 {
+  CFilterElementHideAttrPos m_pos;
+  CFilterElementHideAttrType m_type;
+  std::wstring m_attr;
+  std::wstring m_value;
 
-public:
-
-  CFilterElementHideAttrPos m_pos;
-
-  CFilterElementHideAttrType m_type;
-
-  CComBSTR m_bstrAttr;
-  CString m_value;
-
-  CFilterElementHideAttrSelector();
-  CFilterElementHideAttrSelector(const CFilterElementHideAttrSelector& filter);
-  ~CFilterElementHideAttrSelector();
+  CFilterElementHideAttrSelector()
+    : m_type(TYPE_NONE), m_pos(POS_NONE)
+  {
+  }
 };
 
 
@@ -68,18 +64,17 @@
     TRAVERSER_TYPE_ERROR
   };
 
-
-  CString m_filterText;
+  std::wstring m_filterText;
 
   // For domain specific filters only
-  CString m_tagId;
-  CString m_tagClassName;
-  CString m_tag;
+  std::wstring m_tagId;
+  std::wstring m_tagClassName;
+  std::wstring m_tag;
 
   std::vector<CFilterElementHideAttrSelector> m_attributeSelectors;
   std::shared_ptr<CFilterElementHide> m_predecessor;
 
-  CFilterElementHide(const CString& filterText="");
+  CFilterElementHide(const std::wstring& filterText = L"");
   CFilterElementHide(const CFilterElementHide& filter);
   ETraverserComplexType m_type;
 
@@ -112,7 +107,7 @@
   bool m_isFromStart;
   bool m_isFromEnd;
   int m_hitCount;
-  CString m_filterText;
+  std::wstring m_filterText;
 
   CFilter(const CFilter&);
   CFilter();
@@ -127,13 +122,13 @@
 
 private:
 
-  CString m_dataPath;
+  std::wstring m_dataPath;
 
   // (Tag,Name) -> Filter
-  typedef std::multimap<std::pair<CString,CString>, CFilterElementHide> TFilterElementHideTagsNamed;
+  typedef std::multimap<std::pair<std::wstring, std::wstring>, CFilterElementHide> TFilterElementHideTagsNamed;
 
   // Tag -> Filter
-  typedef std::multimap<CString, CFilterElementHide> TFilterElementHideTags;
+  typedef std::multimap<std::wstring, CFilterElementHide> TFilterElementHideTags;
 
 
   TFilterElementHideTagsNamed m_elementHideTagsId;
@@ -143,9 +138,9 @@
   void ClearFilters();
 
 public:
-  CPluginFilter(const CString& dataPath = "");
+  CPluginFilter(const std::wstring& dataPath = L"");
   bool LoadHideFilters(std::vector<std::wstring> filters);
-  bool AddFilterElementHide(CString filter);
+  bool AddFilterElementHide(std::wstring filter);
   bool IsElementHidden(const std::wstring& tag, IHTMLElement* pEl, const std::wstring& domain, const std::wstring& indent) const;
   HANDLE hideFiltersLoadedEvent;
 };
Index: src/shared/Utils.h
===================================================================
--- a/src/shared/Utils.h
+++ b/src/shared/Utils.h
@@ -69,15 +69,55 @@
 
 std::wstring GetQueryString(const std::wstring& url);
 
+/*
+ * The method used below for trimming strings is taken from here:
+ *   http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
+ */
+template<class C>
+bool isNotWhitespace(C x)
+{
+  return !std::isspace(x, std::locale::classic());
+};
+
+template<class T>
+void TrimStringInPlaceLeft(T& text)
+{
+  text.erase(text.begin(), std::find_if(text.begin(), text.end(), isNotWhitespace<T::value_type>));
+}
+
+template<class T>
+void TrimStringInPlaceRight(T& text)
+{
+  text.erase(std::find_if(text.rbegin(), text.rend(), isNotWhitespace<T::value_type>).base(), text.end());
+}
+
+template<class T>
+void TrimStringInPlace(T& text)
+{
+  TrimStringInPlaceRight(text);
+  TrimStringInPlaceLeft(text);
+}
+
+template<class T>
+T TrimStringLeft(T text)
+{
+  TrimStringInPlaceLeft(text);
+  return text;
+}
+
+template<class T>
+T TrimStringRight(T text)
+{
+  TrimStringInPlaceRight(text);
+  return text;
+}
+
 template<class T>
 T TrimString(T text)
 {
-  // Via http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
-  T trimmed(text);
-  std::function<bool(T::value_type)> isspace = std::bind(&std::isspace<T::value_type>, std::placeholders::_1, std::locale::classic());
-  trimmed.erase(trimmed.begin(), std::find_if(trimmed.begin(), trimmed.end(), std::not1(isspace)));
-  trimmed.erase(std::find_if(trimmed.rbegin(), trimmed.rend(), std::not1(isspace)).base(), trimmed.end());
-  return trimmed;
+  TrimStringInPlaceRight(text);
+  TrimStringInPlaceLeft(text);
+  return text;
 }
 
 #endif // UTILS_H
Index: test/UtilTest.cpp
===================================================================
--- a/test/UtilTest.cpp
+++ b/test/UtilTest.cpp
@@ -19,84 +19,170 @@
 
 namespace
 {
-  void TrimTestBody(std::wstring input, std::wstring expected)
+  void TrimTestBody(const std::wstring& input, const std::wstring& expected)
   {
     std::wstring trimmed = TrimString(input);
-    ASSERT_EQ(expected, trimmed);
+    EXPECT_EQ(expected, trimmed);
+  }
+
+  void TrimLeftTestBody(const std::wstring& input, const std::wstring& expected)
+  {
+    std::wstring trimmed = TrimStringLeft(input);
+    EXPECT_EQ(expected, trimmed);
+  }
+
+  void TrimRightTestBody(const std::wstring& input, const std::wstring& expected)
+  {
+    std::wstring trimmed = TrimStringRight(input);
+    EXPECT_EQ(expected, trimmed);
   }
 }
 
 TEST(TrimTest, Trim00)
 {
-  TrimTestBody(L"", L"");
+  const std::wstring x = L"";
+  TrimTestBody(x, L"");
+  TrimLeftTestBody(x, L"");
+  TrimRightTestBody(x, L"");
 }
 
 TEST(TrimTest, Trim01)
 {
-  TrimTestBody(L" ", L"");
+  const std::wstring x = L" ";
+  TrimTestBody(x, L"");
+  TrimLeftTestBody(x, L"");
+  TrimRightTestBody(x, L"");
 }
 
 TEST(TrimTest, Trim02)
 {
-  TrimTestBody(L"\n", L"");
+  const std::wstring x = L"\n";
+  TrimTestBody(x, L"");
+  TrimLeftTestBody(x, L"");
+  TrimRightTestBody(x, L"");
 }
 
 TEST(TrimTest, Trim03)
 {
-  TrimTestBody(L"\r", L"");
+  const std::wstring x = L"\r";
+  TrimTestBody(x, L"");
+  TrimLeftTestBody(x, L"");
+  TrimRightTestBody(x, L"");
 }
 
 TEST(TrimTest, Trim04)
 {
-  TrimTestBody(L"\t", L"");
+  const std::wstring x = L"\t";
+  TrimTestBody(x, L"");
+  TrimLeftTestBody(x, L"");
+  TrimRightTestBody(x, L"");
 }
 
 TEST(TrimTest, Trim05)
 {
-  TrimTestBody(L"foo", L"foo");
+  const std::wstring x = L"foo";
+  TrimTestBody(x, L"foo");
+  TrimLeftTestBody(x, L"foo");
+  TrimRightTestBody(x, L"foo");
 }
 
 TEST(TrimTest, Trim06)
 {
-  TrimTestBody(L" foo", L"foo");
+  const std::wstring x = L" foo";
+  TrimTestBody(x, L"foo");
+  TrimLeftTestBody(x, L"foo");
+  TrimRightTestBody(x, L" foo");
 }
 
 TEST(TrimTest, Trim07)
 {
-  TrimTestBody(L"\r\nfoo", L"foo");
+  const std::wstring x = L"\r\nfoo";
+  TrimTestBody(x, L"foo");
+  TrimLeftTestBody(x, L"foo");
+  TrimRightTestBody(x, L"\r\nfoo");
 }
 
 TEST(TrimTest, Trim08)
 {
-  TrimTestBody(L"\tfoo", L"foo");
+  const std::wstring x = L"\tfoo";
+  TrimTestBody(x, L"foo");
+  TrimLeftTestBody(x, L"foo");
+  TrimRightTestBody(x, L"\tfoo");
 }
 
 TEST(TrimTest, Trim09)
 {
-  TrimTestBody(L"foo  ", L"foo");
+  const std::wstring x = L"foo  ";
+  TrimTestBody(x, L"foo");
+  TrimLeftTestBody(x, L"foo  ");
+  TrimRightTestBody(x, L"foo");
 }
 
 TEST(TrimTest, Trim10)
 {
-  TrimTestBody(L"foo\r\n", L"foo");
+  const std::wstring x = L"foo\r\n";
+  TrimTestBody(x, L"foo");
+  TrimLeftTestBody(x, L"foo\r\n");
+  TrimRightTestBody(x, L"foo");
 }
 
 TEST(TrimTest, Trim11)
 {
-  TrimTestBody(L"foo\t", L"foo");
+  const std::wstring x = L"foo\t";
+  TrimTestBody(x, L"foo");
+  TrimLeftTestBody(x, L"foo\t");
+  TrimRightTestBody(x, L"foo");
 }
 
 TEST(TrimTest, Trim12)
 {
-  TrimTestBody(L"foo bar", L"foo bar");
+  const std::wstring x = L"foo bar";
+  TrimTestBody(x, L"foo bar");
+  TrimLeftTestBody(x, L"foo bar");
+  TrimRightTestBody(x, L"foo bar");
 }
 
 TEST(TrimTest, Trim13)
 {
-  TrimTestBody(L"foo bar \r\n", L"foo bar");
+  const std::wstring x = L"foo bar \r\n";
+  TrimTestBody(x, L"foo bar");
+  TrimLeftTestBody(x, L"foo bar \r\n");
+  TrimRightTestBody(x, L"foo bar");
 }
 
 TEST(TrimTest, Trim14)
 {
-  TrimTestBody(L"  foo bar \r\n", L"foo bar");
+  const std::wstring x = L"  foo bar \r\n";
+  TrimTestBody(x, L"foo bar");
+  TrimLeftTestBody(x, L"foo bar \r\n");
+  TrimRightTestBody(x, L"  foo bar");
 }
+
+/*
+ * First of two tests verifying that TrimString() does not alter its argument.
+ * This one tests an ordinary, non-const variable as an argument.
+ * It differs from its companion only in the one line that declares the variable used as an argument.
+ */
+TEST(TrimTest, TrimPassesByValue)
+{
+  std::wstring x = L"foo bar  "; // not declared 'const', could alter
+  const std::wstring y = x;
+  ASSERT_EQ(y, x);
+  std::wstring trimmed = TrimString(x); // expect here pass by value
+  EXPECT_EQ(L"foo bar", trimmed);
+  ASSERT_EQ(y, x); // argument variable not altered
+}
+
+/*
+ * Second of two tests verifying that TrimString() does not alter its argument.
+ * This one tests a const variable as an argument.
+ */
+TEST(TrimTest, TrimBindsOnConstArg)
+{
+  const std::wstring x = L"foo bar  "; // declared 'const'
+  const std::wstring y = x;
+  ASSERT_EQ(y, x);
+  std::wstring trimmed = TrimString(x);
+  EXPECT_EQ(L"foo bar", trimmed);
+  ASSERT_EQ(y, x);
+}
