Index: adblockplus.gyp
===================================================================
--- a/adblockplus.gyp
+++ b/adblockplus.gyp
@@ -111,6 +111,7 @@
       'libadblockplus/libadblockplus.gyp:libadblockplus',
     ],
     'sources': [
+      'src/plugin/ActiveQueue.h',
       'src/plugin/AdblockPlus.def',
       'src/plugin/AdblockPlus.idl',
       'src/plugin/AdblockPlus.rc',
@@ -125,12 +126,11 @@
       'src/plugin/Instances.h',
       'src/plugin/NotificationMessage.cpp',
       'src/plugin/NotificationMessage.h',
+      'src/plugin/Placeholder.h',
       'src/plugin/Plugin.cpp',
       'src/plugin/Plugin.h',
       'src/plugin/PluginClass.cpp',
       'src/plugin/PluginClass.h',
-      'src/plugin/PluginClientBase.cpp',
-      'src/plugin/PluginClientBase.h',
       'src/plugin/PluginClientFactory.cpp',
       'src/plugin/PluginClientFactory.h',
       'src/plugin/PluginDebug.cpp',
@@ -248,7 +248,6 @@
       'src/plugin/AdblockPlusDomTraverser.cpp',
       'src/plugin/NotificationMessage.cpp',
       'src/plugin/Plugin.cpp',
-      'src/plugin/PluginClientBase.cpp',
       'src/plugin/PluginClientFactory.cpp',
       'src/plugin/PluginClass.cpp',
       'src/plugin/PluginFilter.cpp',
@@ -287,5 +286,33 @@
         ]],
       },
     },
-  }]
+  },
+
+  {
+    'target_name': 'tests_plugin_2',
+    'type': 'executable',
+    'dependencies': [
+      'libadblockplus/third_party/googletest.gyp:googletest_main',
+    ],
+    'sources': [
+      'src/plugin/ActiveQueue.h',
+      'src/plugin/Placeholder.h',
+      'test/plugin/ActiveQueueTest.cpp',
+      'test/plugin/PlaceholderTest.cpp',
+    ],
+    'defines': [
+    ],
+    'link_settings': {
+      'libraries': [],
+    },
+    #'msbuild_toolset': 'v140',
+    'msvs_settings': {
+      'VCLinkerTool': {
+        'SubSystem': '1', # Console
+        'EntryPointSymbol': 'mainCRTStartup',
+      },
+    },
+  },
+  
+  ]
 }
Index: src/plugin/ActiveQueue.h
===================================================================
new file mode 100644
--- /dev/null
+++ b/src/plugin/ActiveQueue.h
@@ -0,0 +1,281 @@
+/*
+ * This file is part of Adblock Plus <https://adblockplus.org/>,
+ * Copyright (C) 2006-2016 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <deque>
+#include "Placeholder.h"
+
+/**
+ * A synchronized FIFO queue with thread notice on receipt.
+ *
+ * \tparam T
+ *   Class of the elements of the queue.
+ *   It must have a move constructor.
+ */
+template<class T>
+class MessageQueue
+{
+  std::deque<T> queue;
+  typedef std::lock_guard<std::mutex> SentryType;
+  typedef std::unique_lock<std::mutex> UniqueLockType;
+  std::mutex mutex;
+  std::condition_variable cv;
+public:
+  MessageQueue() {} // = default
+
+  /*
+   * This class does not have any responsibility with regard to the elements of its queue.
+   * An instance may be destroyed with elements still in the queue.
+   */
+  ~MessageQueue() {} // = default;
+
+  /**
+   * Insert an element, copy semantics version.
+   */
+  void Insert(const T& t)
+  {
+    SentryType sentry(mutex);
+    queue.push_back(t);
+    cv.notify_one();
+  }
+
+  /**
+   * Insert an element, move semantics version.
+   */
+  void Insert(T&& t)
+  {
+    SentryType sentry(mutex);
+    queue.push_back(std::move(t));
+    cv.notify_one();
+  }
+
+  /**
+   * Wake up anything waiting on the condition variable.
+   *
+   * \tparam Function 
+   *   Functor type for argument
+   * \param f 
+   *   Functor to execute _inside_ the protection of the queue's mutex
+   */
+  template<typename Function>
+  void Rouse(Function& f)
+  {
+    SentryType sentry(mutex);
+    f();
+    cv.notify_one();
+  }
+
+  /**
+   * Test, Remove, and Execute
+   *
+   * If the queue is not empty, remove the next element from the consumer end 
+   *   of the queue and execute the argument functor on it.
+   * 
+   * \param f
+   *   A functor to which to pass the element at the consumer end of the queue.
+   *   This functor is executed _outside_ the protection of the queue's mutex.
+   * \return
+   *   True if and only if an element was removed from the front of the queue.
+   */
+  template<typename Function>
+  bool Remove(Function& f)
+  {
+    /*
+     * We need to remove an element atomically from the queue,
+     *   so we need to construct that element _inside_ the protection of the mutex,
+     * On the other hand, we need to execute the function _outside_ the protection 
+     *   of the queue, because otherwise performance may suffer.
+     * Hence we use 'Placeholder' so that we can declare a variable to hold the 
+     *   removed element without requiring it to have a default constructor or 
+     *   assignment operators.
+     */
+    Placeholder<T> x;
+    {
+      SentryType sentry(mutex);
+      if (queue.empty())
+        return false;
+      x.Construct(std::move(queue.front()));    // only require move-constructibility
+      queue.pop_front();
+    }
+    f(std::move(x.Object()));
+    return true;
+  }
+
+  /**
+   * If a condition is satisfied, wait indefinitely. If not, don't wait.
+   *
+   * \return False, immediately without waiting, if the argument evaluates false.
+   * \return True, only after waiting, if the argument evaluates true.
+   */
+  template<typename Predicate>
+  bool WaitIf(Predicate& p)
+  {
+    UniqueLockType ul(mutex);
+    if (!p())
+    {
+      return false;
+    }
+    cv.wait(ul);
+    return true;
+  }
+
+  /*
+   * A defect in the compiler for VS2012 requires these definitions.
+   */
+#if defined(_MSC_VER) && _MSC_VER <= 1700
+#define WAIT_FOR_RETURN_TYPE std::cv_status::cv_status
+#else
+#define WAIT_FOR_RETURN_TYPE std::cv_status
+#endif
+
+  /**
+   * Wait for a limited duration.
+   *
+   * Return type was going to be "decltype(cv.wait_for)" for clarity.
+   * A defect in the toolset for VS 2012 causes that declaration not to compile.
+   */
+  template <class R, class P>
+  WAIT_FOR_RETURN_TYPE WaitFor(const std::chrono::duration<R,P>& relativeDuration)
+  {
+    return cv.wait_for(UniqueLockType(mutex), relativeDuration);
+  }
+};
+
+/**
+ * An active queue with a single, internal consumer.
+ *
+ * This class presents the front of a message queue for arbitrary producers.
+ * The back of the message queue is managed by an internal consumer,
+ *   which calls the processor for each element passed through the queue.
+ *
+ * The internal thread has the same lifetime as the object.
+ * There's no external interface to pause or kill the thread.
+ * Destroy the object to terminate the thread.
+ * The consumer drains the queue before terminating the thread.
+ *
+ * \tparam T Class of elements in the queue
+ * \tparam F Type of functor to process each element consumed from the queue
+ */
+template<class T, class F>
+class ActiveQueue
+{
+  /**
+   * Signal flag indicating to the consumer thread that it should continue running.
+   *
+   * This flag is true for the entire duration of the object lifetime;
+   *   it's set to false only in the destructor.
+   * To avoid race conditions, it's only accessed under mutex protection with calls to notify and wait.
+   * We accomplish this with lambdas passed as the functor arguments of the message queue functions 'Rouse' and 'WaitIf'.
+   * These lambdas are the only references to the variable after construction.
+   */
+  bool running;
+
+  /**
+   * Functor to process each element as it is removed from the queue.
+   */
+  F& processor;
+
+  /**
+   * The queue that the active thread behavior is wrapped around.
+   */
+  MessageQueue<T> queue;
+
+  /**
+   * Thread running the consumer process.
+   *
+   * This thread runs the entire lifetime of this object.
+   * It's started in the constructor and joined in the destructor.
+   *
+   * This member variable is declared last so that it is constructed last,
+   *   after all the other member necessary for proper functioning of the consumer.
+   */
+  std::thread thread;
+
+  /**
+   * Main function for the consumer thread.
+   *
+   * Strictly speaking, it's the effective main function for the thread.
+   * The actual main function is a lambda that only calls this function and nothing else.
+   */
+  void Consumer()
+  {
+    while (queue.WaitIf([this]() { return running; }))
+    {
+      // Drain the queue, processing all of its elements
+      while (
+        queue.Remove(
+          [&](T&& t) {
+            try
+            {
+              processor(std::move(t));
+            }
+            catch (...)
+            {
+              // Ignore any exception 'processor' may throw.
+            }
+          }
+        )
+      )
+      {
+      }
+      // Loop termination: queue.Remove() removed nothing from the queue, so it's now empty
+    }
+  }
+
+public:
+  ActiveQueue(F& f)
+    : running(true), processor(f), queue(), thread([this]() { Consumer(); })
+  {}
+
+  ~ActiveQueue()
+  {
+    /*
+     * The consumer thread is waiting on the condition variable.
+     * If we don't wake it up, our thread won't terminate.
+     *
+     * Note that we don't have a race condition here with new elements arriving 
+     *   while we are draining the queue for the last time.
+     * We are executing in the destructor, so our member function Insert() is unavailable,
+     *   and it's the only way that new element enter the message queue.
+     */
+    queue.Rouse([this](){ running = false; });
+    thread.join();
+  }
+
+  ActiveQueue(const ActiveQueue&); // = delete
+  ActiveQueue(ActiveQueue&&); // = delete
+  ActiveQueue& operator=(const ActiveQueue&); // = delete
+  ActiveQueue& operator=(ActiveQueue&&); // = delete
+
+  /**
+   * Insert
+   */
+  void Insert(const T& t)
+  {
+    queue.Insert(t);
+  }
+
+  /**
+   * Insert
+   */
+  void Insert(T&& t)
+  {
+    queue.Insert(std::move(t));
+  }
+};
\ No newline at end of file
Index: src/plugin/Config.h
===================================================================
--- a/src/plugin/Config.h
+++ b/src/plugin/Config.h
@@ -28,7 +28,6 @@
 
 #define ENABLE_DEBUG_RESULT
 #define ENABLE_DEBUG_RESULT_IGNORED
-#define ENABLE_DEBUG_SPLIT_FILE
 #else
 #undef ENABLE_DEBUG_INFO
 #endif
@@ -37,8 +36,6 @@
 #undef ENABLE_DEBUG_INFO
 #endif
 
-#undef ENABLE_DEBUG_SELFTEST
-
 #define DEBUG_FUNC CPluginDebug::Debug
 
 #if (defined ENABLE_DEBUG_INFO && defined ENABLE_DEBUG_GENERAL)
@@ -84,18 +81,17 @@
 #endif
 
 #if (defined ENABLE_DEBUG_INFO && defined ENABLE_DEBUG_ERROR)
-#define DEBUG_EXCEPTION(x) CPluginDebug::DebugException(x)
-#define DEBUG_ERROR_CODE(err, x) CPluginDebug::DebugErrorCode(err, x);
-#define DEBUG_ERROR_CODE_EX(err, x, process, thread) CPluginDebug::DebugErrorCode(err, x, process, thread);
+#define DEBUG_EXCEPTION(x) CPluginDebug::DebugException(x, Trace::Location())
+#define DEBUG_ERROR_CODE(err, x) CPluginDebug::DebugErrorCode(err, x, Trace::Location());
+#define DEBUG_ERROR_LOG(err, id, subid, description) Trace::ErrorCode(err, description, Trace::Location(id, subid))
+#define DEBUG_SYSTEM_EXCEPTION(ex, id, subid, description) Trace::SystemException(ex, description, Trace::Location(id, subid))
 #else
 #define DEBUG_EXCEPTION(x)
 #define DEBUG_ERROR_CODE(err, x)
-#define DEBUG_ERROR_CODE_EX(err, x, process, thread)
+#define DEBUG_ERROR_LOG(err, id, subid, description)
+#define DEBUG_SYSTEM_EXCEPTION(ex, id, subid, description)
 #endif
 
-#define DEBUG_ERROR_LOG(err, id, subid, description) LogQueue::PostPluginError(err, id, subid, description);
-#define DEBUG_SYSTEM_EXCEPTION(ex, id, subid, description) CPluginDebug::DebugSystemException(ex, id, subid, description)
-#define DEBUG_SELFTEST(x)
 
 // ----------------------------------------------------------------------------
 // Miscellaneous
Index: src/plugin/Placeholder.h
===================================================================
new file mode 100644
--- /dev/null
+++ b/src/plugin/Placeholder.h
@@ -0,0 +1,78 @@
+/*
+ * This file is part of Adblock Plus <https://adblockplus.org/>,
+ * Copyright (C) 2006-2015 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Allows construction of an object in one block and usage in another.
+ *
+ * Splitting construction and usage is not necessary in all cases,
+ *   but there are many when it is.
+ * In any relevant case it's necessary to define something in a context that contains both blocks.
+ * One is when the class is not default-constructible.
+ * You might be able to construct an initial value if you know the class,
+ *   but that's not the case in general for templates.
+ * Another is when there's no copy assignment available,
+ *   such as std::unique_ptr.
+ *
+ * A 'Placeholder' can be initialized with either a copy or move constructor.
+ * We can't use assignment to initialize, because (in general) assignment assumes
+ *   that the lvalue target is already initialized.
+ */
+template<class T>
+class Placeholder
+{
+  /**
+   * We 'allocate' memory for the object in the instance itself;
+   *   there's no use of the heap.
+   *
+   * Note that it's a normative standard that sizeof(char)==1 
+   */
+  char storage[sizeof(T)];
+
+public:
+  /**
+   * Initialize by copying a glvalue into our private storage.
+   */
+  void Construct(const T& x)
+  {
+    /*
+     * We use placement-new syntax to use our private storage.
+     * The constructor expression invokes the copy constructor.
+     */
+    new (&storage[0]) T(x);
+  }
+
+  /**
+   * Initialize by moving an rvalue into our private storage.
+   */
+  void Construct(T&& x)
+  {
+    /*
+     * We use placement-new syntax to use our private storage.
+     * We explicitly cast to an rvalue to ensure use of the move constructor.
+     */
+    new (&storage[0]) T(std::move(x));
+  }
+
+  /**
+   * Access the object in storage.
+   */
+  T& Object()
+  {
+    return *reinterpret_cast<T *>(&storage[0]);
+  }
+};
+
Index: src/plugin/PluginClass.cpp
===================================================================
--- a/src/plugin/PluginClass.cpp
+++ b/src/plugin/PluginClass.cpp
@@ -23,7 +23,6 @@
 #include "PluginFilter.h"
 #include "PluginMimeFilterClient.h"
 #include "AdblockPlusClient.h"
-#include "PluginClientBase.h"
 #include "PluginClientFactory.h"
 #include "PluginUtil.h"
 #include "../shared/Utils.h"
@@ -486,7 +485,7 @@
             hr = browser->Navigate(CComBSTR(curLoc), &vFlags, NULL, NULL, NULL);
             if (FAILED(hr))
             {
-              DEBUG_ERROR_LOG(hr, PLUGIN_ERROR_NAVIGATION, PLUGIN_ERROR_NAVIGATION, "Navigation::Failed")
+              DEBUG_ERROR_LOG(hr, PLUGIN_ERROR_NAVIGATION, PLUGIN_ERROR_NAVIGATION, "Navigation::Failed");
             }
           }
           browser->Quit();
@@ -787,7 +786,7 @@
   HWND hBrowserWnd = GetBrowserHWND();
   if (!hBrowserWnd)
   {
-    DEBUG_ERROR_LOG(0, PLUGIN_ERROR_UI, PLUGIN_ERROR_UI_NO_STATUSBAR_BROWSER, "Class::CreateStatusBarPane - No status bar")
+    TRACE("No window handle for browser site", HERE_F);
     return false;
   }
 
@@ -869,7 +868,7 @@
 
   if (!hWndStatusBar)
   {
-    DEBUG_ERROR_LOG(0, PLUGIN_ERROR_UI, PLUGIN_ERROR_UI_NO_STATUSBAR_WIN, "Class::CreateStatusBarPane - No status bar")
+    TRACE("No status bar", HERE_F);
     return true;
   }
 
@@ -913,7 +912,7 @@
 
   if (!hWndNewPane)
   {
-    DEBUG_ERROR_LOG(::GetLastError(), PLUGIN_ERROR_UI, PLUGIN_ERROR_UI_CREATE_STATUSBAR_PANE, "Class::CreateStatusBarPane - CreateWindowEx")
+    DEBUG_ERROR_LOG(::GetLastError(), PLUGIN_ERROR_UI, PLUGIN_ERROR_UI_CREATE_STATUSBAR_PANE, "Class::CreateStatusBarPane - CreateWindowEx");
     return false;
   }
 
@@ -969,7 +968,7 @@
 
   if (FAILED(hr))
   {
-    DEBUG_ERROR_LOG(hr, PLUGIN_ERROR_NAVIGATION, PLUGIN_ERROR_NAVIGATION_WELCOME, "Navigation::Welcome page failed")
+    DEBUG_ERROR_LOG(hr, PLUGIN_ERROR_NAVIGATION, PLUGIN_ERROR_NAVIGATION_WELCOME, "Navigation::Welcome page failed");
   }
 }
 void CPluginClass::CloseTheme()
@@ -1147,7 +1146,7 @@
           hr = browser->Navigate(urlToNavigate, &vFlags, NULL, NULL, NULL);
           if (FAILED(hr))
           {
-            DEBUG_ERROR_LOG(hr, PLUGIN_ERROR_NAVIGATION, PLUGIN_ERROR_NAVIGATION_SETTINGS, "Navigation::Failed")
+            DEBUG_ERROR_LOG(hr, PLUGIN_ERROR_NAVIGATION, PLUGIN_ERROR_NAVIGATION_SETTINGS, "Navigation::Failed");
           }
         }
       }
Index: src/plugin/PluginClass.h
===================================================================
--- a/src/plugin/PluginClass.h
+++ b/src/plugin/PluginClass.h
@@ -24,6 +24,7 @@
 
 
 #include "Plugin.h"
+#include "PluginDebug.h"
 #include "PluginTabBase.h"
 #define _CRTDBG_MAP_ALLOC
 #include <stdlib.h>
@@ -52,6 +53,11 @@
   friend class CPluginTab;
 
 private:
+  /**
+   * Existence of this object ensures the existence of the log. 
+   * Defined first so that the log is available to the constructors of other members.
+   */
+  LogQueueReference lqr;
 
   CPluginTab* m_tab;
 
Index: src/plugin/PluginClientBase.cpp
===================================================================
deleted file mode 100644
--- a/src/plugin/PluginClientBase.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * This file is part of Adblock Plus <https://adblockplus.org/>,
- * Copyright (C) 2006-2016 Eyeo GmbH
- *
- * Adblock Plus is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3 as
- * published by the Free Software Foundation.
- *
- * Adblock Plus is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "PluginClientBase.h"
-
-#include "PluginSettings.h"
-#include "Config.h"
-#include "PluginDebug.h"
-
-CComAutoCriticalSection LogQueue::s_criticalSectionQueue;
-std::vector<CPluginError> LogQueue::s_pluginErrors;
-
-void LogQueue::LogPluginError(DWORD errorCode, int errorId, int errorSubid, const std::string& description, bool isAsync, DWORD dwProcessId, DWORD dwThreadId)
-{
-  // Prevent circular references
-  if (CPluginSettings::HasInstance() && isAsync)
-  {
-    DEBUG_ERROR_CODE_EX(errorCode, description, dwProcessId, dwThreadId);
-
-    CString pluginError;
-    pluginError.Format(L"%2.2d%2.2d", errorId, errorSubid);
-
-    CString pluginErrorCode;
-    pluginErrorCode.Format(L"%u", errorCode);
-
-    CPluginSettings* settings = CPluginSettings::GetInstance();
-
-    settings->AddError(pluginError, pluginErrorCode);
-  }
-
-  // Post error to client for later submittal
-  if (!isAsync)
-  {
-    LogQueue::PostPluginError(errorId, errorSubid, errorCode, description);
-  }
-}
-
-
-void LogQueue::PostPluginError(int errorId, int errorSubid, DWORD errorCode, const std::string& errorDescription)
-{
-  s_criticalSectionQueue.Lock();
-  {
-    CPluginError pluginError(errorId, errorSubid, errorCode, errorDescription);
-
-    s_pluginErrors.push_back(pluginError);
-  }
-  s_criticalSectionQueue.Unlock();
-}
-
-
-bool LogQueue::PopFirstPluginError(CPluginError& pluginError)
-{
-  bool hasError = false;
-
-  s_criticalSectionQueue.Lock();
-  {
-    std::vector<CPluginError>::iterator it = s_pluginErrors.begin();
-    if (it != s_pluginErrors.end())
-    {
-      pluginError = *it;
-
-      hasError = true;
-
-      s_pluginErrors.erase(it);
-    }
-  }
-  s_criticalSectionQueue.Unlock();
-
-  return hasError;
-}
Index: src/plugin/PluginClientBase.h
===================================================================
deleted file mode 100644
--- a/src/plugin/PluginClientBase.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * This file is part of Adblock Plus <https://adblockplus.org/>,
- * Copyright (C) 2006-2016 Eyeo GmbH
- *
- * Adblock Plus is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3 as
- * published by the Free Software Foundation.
- *
- * Adblock Plus is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef _PLUGIN_CLIENT_BASE_H_
-#define _PLUGIN_CLIENT_BASE_H_
-
-#include <vector>
-#include "ATL_Deprecate.h"
-
-class CPluginError
-{
-
-private:
-
-  int m_errorId;
-  int m_errorSubid;
-  DWORD m_errorCode;
-  std::string m_errorDescription;
-  DWORD m_processId;
-  DWORD m_threadId;
-
-public:
-
-  CPluginError(int errorId, int errorSubid, DWORD errorCode, const std::string& errorDesc) : 
-    m_errorId(errorId), m_errorSubid(errorSubid), m_errorCode(errorCode), m_errorDescription(errorDesc)
-  {
-    m_processId = ::GetCurrentProcessId();
-    m_threadId = ::GetCurrentThreadId();
-  }
-
-  CPluginError() : 
-    m_errorId(0), m_errorSubid(0), m_errorCode(0), m_processId(0), m_threadId(0) {}
-
-  CPluginError(const CPluginError& org) : 
-    m_errorId(org.m_errorId), m_errorSubid(org.m_errorSubid), m_errorCode(org.m_errorCode), m_errorDescription(org.m_errorDescription), m_processId(org.m_processId), m_threadId(org.m_threadId) {}
-
-  int GetErrorId() const { return m_errorId; }
-  int GetErrorSubid() const { return m_errorSubid; }
-  DWORD GetErrorCode() const { return m_errorCode; }
-  std::string GetErrorDescription() const { return m_errorDescription; }
-  DWORD GetProcessId() const { return m_processId; }
-  DWORD GetThreadId() const { return m_threadId; }
-};
-
-
-class LogQueue
-{
-private:
-  static std::vector<CPluginError> s_pluginErrors;
-  static CComAutoCriticalSection s_criticalSectionQueue;
-
-public:
-  static void LogPluginError(DWORD errorCode, int errorId, int errorSubid, const std::string& description="", bool isAsync=false, DWORD dwProcessId=0, DWORD dwThreadId=0);
-  static void PostPluginError(int errorId, int errorSubid, DWORD errorCode, const std::string& errorDescription);
-  static bool PopFirstPluginError(CPluginError& pluginError);
-};
-
-#endif // _PLUGIN_CLIENT_BASE_H_
Index: src/plugin/PluginDebug.cpp
===================================================================
--- a/src/plugin/PluginDebug.cpp
+++ b/src/plugin/PluginDebug.cpp
@@ -17,13 +17,24 @@
 
 #include "PluginStdAfx.h"
 #include "PluginDebug.h"
-#include "PluginClientBase.h"
+#include "ActiveQueue.h"
 #include "PluginMutex.h"
-#include "PluginClientBase.h"
 #include "../shared/Utils.h"
+#include <Windows.h>
 #include <iomanip>
 #include <memory>
 
+namespace Trace
+{
+  Location::Location(int id, int subId)
+    : prefixText()
+  {
+    std::ostringstream ss;
+    ss << "ErrorCode(" << id << "," << subId << ")";
+    postfixText = ss.str();
+  };
+}
+
 namespace
 {
   class CPluginDebugLock
@@ -47,6 +58,9 @@
 
   CComAutoCriticalSection CPluginDebugLock::s_criticalSectionDebugLock;
 
+  /**
+   * The polymorphic part of a log entry.
+   */
   class LogText
   {
   public:
@@ -108,9 +122,14 @@
     }
   };
 
+  /**
+   * Standard log entry, with variation in the data that generates the log message.
+   */
   class LogEntry
   {
-    const std::unique_ptr<LogText> text;
+    std::unique_ptr<LogText> text;
+
+    const Trace::Location location;
 
     std::string InitialPrefix() const
     {
@@ -149,14 +168,27 @@
      */
     const DWORD threadId;
 
-    explicit LogEntry(LogText* text)
-      : processId(::GetCurrentProcessId()), threadId(::GetCurrentThreadId()), text(text)
+    explicit LogEntry(LogText* text, Trace::Location location)
+      : text(text), location(location), processId(::GetCurrentProcessId()), threadId(::GetCurrentThreadId())
     {}
 
-    LogEntry(LogText* text, DWORD processId, DWORD threadId)
-      : processId(processId), threadId(threadId), text(text)
+    /**
+     * Move constructor transfers 'text', copies everything else.
+     */
+    LogEntry(LogEntry&& x)
+      : text(std::move(x.text)), location(x.location), st(x.st), processId(x.processId), threadId(x.threadId)
     {}
 
+    /**
+     * Copy constructor deleted.
+     */
+    LogEntry(const LogEntry&); // = delete
+
+    /**
+     * Copy assignment deleted.
+     */
+    LogEntry& operator=(const LogEntry&); // = delete
+
     void Write(std::ostream& out) const
     {
       CPluginDebugLock lock;
@@ -166,19 +198,49 @@
         size_t linePosition = 0;
         while (true)
         {
-          auto eolPosition = lines.find('\n', linePosition);
+          // Standard prefix, timestamp and thread ID
           auto prefix = linePosition == 0 ? InitialPrefix() : SubsequentPrefix();
           out << prefix;
+
+          // Location prefix, if any
+          if (location.prefixText.length() > 0)
+          {
+            out << location.prefixText << " - ";
+          }
+
+          // One line of the log text itself
+          auto eolPosition = lines.find('\n', linePosition);
           if (eolPosition == std::string::npos)
           {
-            out << lines.substr(linePosition) << "\n";
-            break;
+            out << lines.substr(linePosition);
           }
           else
           {
-            out << lines.substr(linePosition, eolPosition - linePosition) << "\n";
+            out << lines.substr(linePosition, eolPosition - linePosition);
             linePosition = eolPosition + 1;
           }
+
+          // Location postfix, if any
+          if (location.postfixText.length() > 0)
+          {
+            out << " - ";
+            // Hack to deal with how gyp generates file names for Visual Studio
+            if (location.postfixText.find("..\\..\\") == 0)
+            {
+              out << location.postfixText.substr(6);
+            }
+            else
+            {
+              out << location.postfixText;
+            }
+          }
+          out << "\n";
+
+          // Check to see if that was the last line
+          if (eolPosition == std::string::npos)
+          {
+            break;
+          }
         }
         out.flush();
       }
@@ -209,12 +271,97 @@
     le.WriteFile(debugFileName);
   }
 }
-  
+
+/**
+ * An active queue of 'LogEntry'.
+ * Life cycle is maintained in conjunction with 'LogQueueReference'.
+ */
+class LogQueue
+{
+  /**
+   * Processor for the log queue
+   */
+  static void ProcessLogEntry(const LogEntry&);
+
+  /**
+   * Active queue for processing log entries.
+   */
+  ActiveQueue<LogEntry, decltype(ProcessLogEntry)> queue;
+
+public:
+  /**
+   * Insert an item into the queue.
+   */
+  static void Insert(LogEntry&& t)
+  {
+    /*
+     * Note that this function(indeed, this class) does not have the
+     *   responsibility to ensure that an instance of the queue exists.
+     * See 'LogQueueReference' for the class that does.
+     * The non-null guard here is purely defensive;
+     *   we shouldn't ever lose queued events because of it.
+     */
+    if (LogQueueReference::queueMasterReference)
+    {
+      LogQueueReference::queueMasterReference->queue.Insert(std::move(t));
+    }
+  }
+
+  LogQueue()
+    : queue(ProcessLogEntry)
+  {}
+};
+
+/**
+ * The processor for the log queue simply writes each log entry to its corresponding log file.
+ *
+ * At present the only log entries that go through the queue also go to the default log file.
+ * This logic will expand to include other log files as we pass all the log entries through the queue.
+ */
+void LogQueue::ProcessLogEntry(const LogEntry& entry)
+{
+  LogWriteDefault(entry);
+}
+
+/*
+ * Static initializations
+ */
+LogQueueReference::pointer LogQueueReference::queueMasterReference;
+std::mutex LogQueueReference::mutex;
+
+/*
+ * Only initialize the instance reference within the constructor body,
+ *   so that it's serialized under the protection of the mutex.
+ */
+LogQueueReference::LogQueueReference()
+  : queueReference(queueMasterReference)
+{
+  std::unique_lock<std::mutex> lock(LogQueueReference::mutex);
+  if (!queueMasterReference)
+  {
+    queueMasterReference = pointer(new LogQueue());
+    LogQueue::Insert(LogEntry(new LogTextFixed("LogQueue start"), HERE_F));
+  }
+  queueReference = queueMasterReference;
+}
+
+LogQueueReference::~LogQueueReference()
+{
+  std::unique_lock<std::mutex> lock(LogQueueReference::mutex);
+  queueReference.reset();
+  if (queueMasterReference.use_count() == 1)
+  {
+    LogQueue::Insert(LogEntry(new LogTextFixed("LogQueue stop"), HERE_F));
+    queueMasterReference.reset();
+  }
+}
+
+
 #ifdef ENABLE_DEBUG_INFO
 
 void CPluginDebug::Debug(const std::string& text)
 {
-  LogWriteDefault(LogEntry(new LogTextFixed(text)));
+  LogWriteDefault(LogEntry(new LogTextFixed(text), Trace::Location()));
 }
 
 void CPluginDebug::Debug(const std::wstring& text)
@@ -224,38 +371,29 @@
 
 #endif
 
-void CPluginDebug::DebugSystemException(const std::system_error& ex, int errorId, int errorSubid, const std::string& description)
+namespace Trace
 {
-  std::string message = description + ", " + ex.code().message() + ", " + ex.what();
-  DEBUG_ERROR_LOG(ex.code().value(), errorId, errorSubid, message);
+  void SystemException(const std::system_error& ex, const std::string& description, Trace::Location location)
+  {
+    std::string message = description + ", " + ex.code().message() + ", " + ex.what();
+    ErrorCode(ex.code().value(), message, location);
+  }
 }
 
 #if (defined ENABLE_DEBUG_INFO)
 
-void CPluginDebug::DebugException(const std::exception& ex)
+void CPluginDebug::DebugException(const std::exception& ex, Trace::Location location)
 {
   auto lt = new LogTextFixed(ex);
-  LogEntry le(lt);
-#ifdef ENABLE_DEBUG_ERROR
+  LogEntry le(lt, location);
   LogWriteDefault(le);
-#endif
-  DEBUG_SELFTEST(
-    "********************************************************************************\n"
-    + lt->text() + "\n"
-    "********************************************************************************")
 }
 
-void CPluginDebug::DebugErrorCode(DWORD errorCode, const std::string& error, DWORD processId, DWORD threadId)
+void CPluginDebug::DebugErrorCode(DWORD errorCode, const std::string& error, Trace::Location location)
 {
   auto lt = new LogTextErrorCode(errorCode, error);
-  LogEntry le(lt, processId, threadId);
-#ifdef ENABLE_DEBUG_ERROR
+  LogEntry le(lt, location);
   LogWriteDefault(le);
-#endif
-  DEBUG_SELFTEST(
-    "********************************************************************************\n"
-    + lt->text() + "\n"
-    "********************************************************************************")
 }
 
 #endif
@@ -268,7 +406,7 @@
 
 void CPluginDebug::DebugResult(const std::wstring& text)
 {
-  LogWriteResult(LogEntry(new LogTextFixed(ToUtf8String(text))));
+  LogWriteResult(LogEntry(new LogTextFixed(ToUtf8String(text)), Trace::Location()));
 }
 
 void CPluginDebug::DebugResultDomain(const std::wstring& domain)
@@ -307,7 +445,6 @@
   DebugResultFormat(L"Blocked", type, domain.empty() ? L"-" : domain, Shorten(src));
 }
 
-
 void CPluginDebug::DebugResultHiding(const std::wstring& tag, const std::wstring& id, const std::wstring& filter)
 {
   DebugResultFormat(L"Hidden", tag, L"- " + Shorten(id), filter);
@@ -315,7 +452,6 @@
 
 #endif // ENABLE_DEBUG_RESULT
 
-
 #ifdef ENABLE_DEBUG_RESULT_IGNORED
 
 void CPluginDebug::DebugResultIgnoring(const std::wstring& type, const std::wstring& src, const std::wstring& domain)
@@ -325,6 +461,16 @@
 
 #endif // ENABLE_DEBUG_RESULT_IGNORED
 
+void Trace::ErrorCode(DWORD errorCode, const std::string& description, Trace::Location location)
+{
+  LogQueue::Insert(LogEntry(new LogTextErrorCode(errorCode, description), location));
+}
+
+void Trace::TextFixed(const std::string& description, Trace::Location location)
+{
+  LogQueue::Insert(LogEntry(new LogTextFixed(description), location));
+}
+
 namespace
 {
   /*
Index: src/plugin/PluginDebug.h
===================================================================
--- a/src/plugin/PluginDebug.h
+++ b/src/plugin/PluginDebug.h
@@ -18,19 +18,66 @@
 #ifndef _PLUGIN_DEBUG_H_
 #define _PLUGIN_DEBUG_H_
 
+#include <memory>
+#include <mutex>
 #include <string>
 
+namespace Trace
+{
+  /**
+   */
+  struct Location
+  {
+    std::string prefixText;
+    std::string postfixText;
+
+    Location() // = default
+      : prefixText(), postfixText()
+    {};
+
+    Location(int id, int subId);
+
+    Location(std::string postfixText)
+      : prefixText(), postfixText(postfixText)
+    {}
+
+    Location(std::string prefixText, std::string postfixText)
+      : prefixText(prefixText), postfixText(postfixText)
+    {}
+  };
+
+  void TextFixed(const std::string& description, Trace::Location location);
+  void ErrorCode(DWORD errorCode, const std::string& description, Trace::Location location);
+  void SystemException(const std::system_error& ex, const std::string& description, Trace::Location location);
+}
+
+// Cope with insufficient support on old toolsets
+#if !defined(__func__) && defined(_MSC_VER)
+#define __func__ __FUNCTION__
+#endif
+
+// __LINE__ expands to an integer literal, not a string literal
+// The stringify operator "#" applies only to arguments, not macro definitions
+#define STRING_EXPAND(x) STRINGIFY(x)
+#define STRINGIFY(x) #x
+#define __LINE_STRING__ STRING_EXPAND(__LINE__)
+#define HERE Trace::Location(__FILE__ ":" __LINE_STRING__)
+#define HERE_F Trace::Location(__func__, __FILE__ ":" __LINE_STRING__)
+
+#ifdef _DEBUG
+#define TRACE(description, location) Trace::TextFixed(description, location)
+#else
+#define TRACE(a,b)
+#endif
+
 class CPluginDebug
 {
-
 public:
-  static void DebugSystemException(const std::system_error& ex, int errorId, int errorSubid, const std::string& description); 
-
 #if (defined ENABLE_DEBUG_INFO)
   static void Debug(const std::string& text);
   static void Debug(const std::wstring& text);
-  static void DebugException(const std::exception& ex);
-  static void DebugErrorCode(DWORD errorCode, const std::string& error, DWORD processId=0, DWORD threadId=0);
+  static void DebugException(const std::exception& ex, Trace::Location location);
+  static void DebugErrorCode(DWORD errorCode, const std::string& error, Trace::Location location);
 #endif
 
 #if (defined ENABLE_DEBUG_RESULT)
@@ -53,4 +100,59 @@
 */
 std::wstring ToHexLiteral(const void*);
 
+/*
+ * Forward declaration.
+ */
+class LogQueue;
+
+/**
+ * This class maintains a singleton LogQueue in existence.
+ *
+ * This class exists because it's not possible to use a static declaration for
+ *  'std::thread' or any class that contains one, such as 'ActiveQueue'.
+ * To appreciate of the origin of restriction, see 
+ *   http://stackoverflow.com/questions/28746016/thread-join-does-not-return-when-called-in-global-var-destructor
+ * In addition, we have no main() function to use for initialization,
+ *   since we're packaged as a COM class provider.
+ *
+ * As a substitute, we maintain a reference to the queue in each instance of
+ *   'CPluginClass', the sole visible COM class, the one that implements the
+ *   BHO interface.
+ * This class has the responsibility for managing the life cycle of the queue,
+ *   ensuring it exists whenever there's a reference to it.
+ * The lifespan of instances of 'CPluginClass' encompasses the lifespans of any
+ *   other objects that the BHO uses, so it's sufficient to maintain a LogQueue
+ *   in existence with a reference in that class.
+ *
+ * The queue itself is accessible only to the logging functions;
+ *   it need not be visible externally.
+ * Hence all this class requires is an opaque, forward declaration.
+ * Mere instantiation of this class is sufficient to ensure we have a log queue.
+ */
+class LogQueueReference
+{
+  friend LogQueue;
+
+  typedef std::shared_ptr<LogQueue> pointer;
+
+  /**
+   * The class-wide reference to the debug queue.
+   */
+  static pointer queueMasterReference;
+
+  /**
+   * The instance reference to the debug queue.
+   */
+  pointer queueReference;
+
+  /**
+   * Mutex to allow joint serialization of constructor/destructor calls.
+   */
+  static std::mutex mutex;
+
+public:
+  LogQueueReference();
+  ~LogQueueReference();
+};
+
 #endif // _PLUGIN_DEBUG_H_
Index: src/plugin/PluginMimeFilterClient.cpp
===================================================================
--- a/src/plugin/PluginMimeFilterClient.cpp
+++ b/src/plugin/PluginMimeFilterClient.cpp
@@ -17,7 +17,6 @@
 
 #include "PluginStdAfx.h"
 #include "PluginMimeFilterClient.h"
-#include "PluginClientBase.h"
 #include "PluginWbPassThrough.h"
 
 CPluginMimeFilterClient::CPluginMimeFilterClient() : m_classFactory(NULL), m_spCFHTTP(NULL),  m_spCFHTTPS(NULL)
Index: src/plugin/PluginMutex.cpp
===================================================================
--- a/src/plugin/PluginMutex.cpp
+++ b/src/plugin/PluginMutex.cpp
@@ -16,10 +16,7 @@
  */
 
 #include "PluginStdAfx.h"
-
 #include "PluginMutex.h"
-#include "PluginClientBase.h"
-
 
 CPluginMutex::CPluginMutex(const std::wstring& name, int errorSubidBase) 
   : m_isLocked(false), m_errorSubidBase(errorSubidBase), system_name(L"Global\\AdblockPlus" + name)
Index: src/plugin/PluginSettings.cpp
===================================================================
--- a/src/plugin/PluginSettings.cpp
+++ b/src/plugin/PluginSettings.cpp
@@ -71,19 +71,6 @@
 }
 
 
-bool CPluginSettings::HasInstance()
-{
-  bool hasInstance = true;
-
-  s_criticalSectionLocal.Lock();
-  {
-    hasInstance = s_instance != NULL;
-  }
-  s_criticalSectionLocal.Unlock();
-
-  return hasInstance;
-}
-
 bool CPluginSettings::IsPluginEnabled() const
 {
   return GetPluginEnabled();
@@ -111,11 +98,6 @@
   return CPluginClient::GetInstance()->GetPref(L"enabled", true);
 }
 
-void CPluginSettings::AddError(const CString& error, const CString& errorCode)
-{
-  DEBUG_SETTINGS(L"SettingsTab::AddError error:" + error + " code:" + errorCode)
-}
-
 // ============================================================================
 // Whitelist settings
 // ============================================================================
Index: src/plugin/PluginSettings.h
===================================================================
--- a/src/plugin/PluginSettings.h
+++ b/src/plugin/PluginSettings.h
@@ -64,7 +64,6 @@
 
   static CPluginSettings* s_instance;
 
-  static bool HasInstance();
   static CPluginSettings* GetInstance();
 
   bool IsPluginEnabled() const;
@@ -76,8 +75,6 @@
   void TogglePluginEnabled();
   bool GetPluginEnabled() const;
 
-  void AddError(const CString& error, const CString& errorCode);
-
   // Settings whitelist
 private:
   std::vector<std::wstring> m_whitelistedDomains;
Index: src/plugin/PluginSystem.cpp
===================================================================
--- a/src/plugin/PluginSystem.cpp
+++ b/src/plugin/PluginSystem.cpp
@@ -17,7 +17,6 @@
 
 #include "PluginStdAfx.h"
 #include "PluginSystem.h"
-#include "PluginClientBase.h"
 #include <array>
 
 std::wstring GetBrowserLanguage()
Index: src/plugin/PluginTabBase.cpp
===================================================================
--- a/src/plugin/PluginTabBase.cpp
+++ b/src/plugin/PluginTabBase.cpp
@@ -17,7 +17,6 @@
 
 #include "PluginStdAfx.h"
 #include "AdblockPlusClient.h"
-#include "PluginClientBase.h"
 #include "PluginSettings.h"
 #include "AdblockPlusDomTraverser.h"
 #include "PluginTabBase.h"
@@ -26,40 +25,17 @@
 #include <Mshtmhst.h>
 
 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)
-  {
-    m_isActivated = true;
-  }
-
-  try
-  {
-    m_thread = std::thread(&CPluginTab::ThreadProc, this);
-  }
-  catch (const std::system_error& ex)
-  {
-    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();
-  }
-}
-
 /**
  * ABP only intercepts protocols "http:" and "https:".
  * We can disable any domain used in those protocol with an appropriate whitelist filter.
@@ -71,15 +47,6 @@
   return BeginsWith(url, L"http:") || BeginsWith(url, L"https:");
 }
 
-void CPluginTab::OnActivate()
-{
-  m_isActivated = true;
-}
-
-
-void CPluginTab::OnUpdate()
-{
-  m_isActivated = true;
 }
 
 namespace
@@ -394,59 +361,3 @@
   m_criticalSectionCache.Unlock();
 }
 
-void CPluginTab::ThreadProc()
-{
-  // Force loading/creation of settings
-  CPluginSettings::GetInstance();
-
-  std::string message =
-    "================================================================================\n"
-    "TAB THREAD process=";
-  message += std::to_string(::GetCurrentProcessId());
-  message + " thread=";
-  message += std::to_string(::GetCurrentThreadId());
-  message +=
-    "\n"
-    "================================================================================";
-  DEBUG_GENERAL(message);
-
-  // --------------------------------------------------------------------
-  // Tab loop
-  // --------------------------------------------------------------------
-
-  DWORD loopCount = 0;
-  DWORD tabLoopIteration = 1;
-
-  while (this->m_continueThreadRunning)
-  {
-#ifdef ENABLE_DEBUG_THREAD
-    CStringA sTabLoopIteration;
-    sTabLoopIteration.Format("%u", tabLoopIteration);
-
-    DEBUG_THREAD("--------------------------------------------------------------------------------")
-      DEBUG_THREAD("Loop iteration " + sTabLoopIteration);
-    DEBUG_THREAD("--------------------------------------------------------------------------------")
-#endif
-      this->m_isActivated = false;
-
-      // --------------------------------------------------------------------
-      // End loop
-      // --------------------------------------------------------------------
-
-      // Sleep loop
-      while (this->m_continueThreadRunning && !this->m_isActivated && (++loopCount % (TIMER_THREAD_SLEEP_TAB_LOOP / 50)) != 0)
-      {
-        // Post async plugin error
-        CPluginError pluginError;
-        if (LogQueue::PopFirstPluginError(pluginError))
-        {
-          LogQueue::LogPluginError(pluginError.GetErrorCode(), pluginError.GetErrorId(), pluginError.GetErrorSubid(), pluginError.GetErrorDescription(), true, pluginError.GetProcessId(), pluginError.GetThreadId());
-        }
-
-        // Non-hanging sleep
-        Sleep(50);
-      }
-
-      tabLoopIteration++;
-  }
-}
Index: src/plugin/PluginTabBase.h
===================================================================
--- a/src/plugin/PluginTabBase.h
+++ b/src/plugin/PluginTabBase.h
@@ -34,15 +34,10 @@
   std::wstring m_documentDomain;
   std::wstring m_documentUrl;
   CPluginUserSettings m_pluginUserSettings;
-  bool m_isActivated;
-
-  std::thread m_thread;
-  std::atomic<bool> m_continueThreadRunning;
   CPluginDomTraverser* m_traverser;
 public:
   CPluginFilter m_filter;
 private:
-  void ThreadProc();
   CComAutoCriticalSection m_criticalSectionCache;
   std::set<std::wstring> m_cacheFrames;
   std::wstring m_cacheDomain;
@@ -55,12 +50,9 @@
   std::wstring GetDocumentDomain();
   void SetDocumentUrl(const std::wstring& url);
   std::wstring GetDocumentUrl();
-  virtual void OnActivate();
-  virtual void OnUpdate();
   virtual void OnNavigate(const std::wstring& url);
   virtual void OnDownloadComplete(IWebBrowser2* browser);
   virtual void OnDocumentComplete(IWebBrowser2* browser, const std::wstring& url, bool isDocumentBrowser);
-  static DWORD WINAPI TabThreadProc(LPVOID pParam);
   void CacheFrame(const std::wstring& url);
   bool IsFrameCached(const std::wstring& url);
   void ClearFrameCache(const std::wstring& domain=L"");
Index: test/plugin/ActiveQueueTest.cpp
===================================================================
new file mode 100644
--- /dev/null
+++ b/test/plugin/ActiveQueueTest.cpp
@@ -0,0 +1,183 @@
+/*
+ * This file is part of Adblock Plus <https://adblockplus.org/>,
+ * Copyright (C) 2006-2015 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <gtest/gtest.h>
+#include "../../src/plugin/ActiveQueue.h"
+#include <ostream>
+#include <memory>
+
+class TrivialIntMessageQueue
+  : public MessageQueue<int>
+{
+public:
+  TrivialIntMessageQueue()
+    : MessageQueue()
+  {}
+};
+
+TEST(MessageQueue, Instance)
+{
+  TrivialIntMessageQueue mq;
+}
+
+TEST(MessageQueue, Insert)
+{
+  TrivialIntMessageQueue mq;
+  mq.Insert(5);
+}
+
+TEST(MessageQueue, RvalueInsertRemove)
+{
+  TrivialIntMessageQueue mq;
+  mq.Insert(2+3); // int&&
+  int result;
+  auto haveResult = mq.Remove([&](int x) { result = x; });
+  ASSERT_TRUE(haveResult);
+  ASSERT_EQ(5, result);
+}
+
+TEST(MessageQueue, ConstInsertRemove)
+{
+  TrivialIntMessageQueue mq;
+  const int value = 4;
+  mq.Insert(value); // const int&
+  int result;
+  auto haveResult = mq.Remove([&](int x) { result = x; });
+  ASSERT_TRUE(haveResult);
+  ASSERT_EQ(4, result);
+}
+
+TEST(MessageQueue, Thread)
+{
+  bool threadTimeout = false;
+  std::string threadFailMessage;
+  MessageQueue<unsigned int> mq;
+  static const unsigned int numberOfElements = 1000;
+  unsigned int counter = 1;
+
+  std::thread th([&]() -> void {
+    // Begin body of notice thread
+    try
+    {
+      /*
+       * We use a nested loop with the same predicate because notice and wait are not related one-to-one.
+       * Multiple calls to notify_one() may happen before the wait_for() statement is active.
+       * Thus the outer loop manages the condition variable.
+       * The inner loop removes all available queue elements, since there may be more than one per notice.
+       */
+      while (counter < numberOfElements)
+      {
+        if (mq.WaitFor(std::chrono::seconds(10)) == std::cv_status::timeout)
+        {
+          threadFailMessage = "Thread timed out.";
+          return;
+        }
+        bool noticeIsFresh = true;
+        while (counter < numberOfElements)
+        {
+          unsigned int element;
+          auto f = [&](unsigned int x) -> void { element = x; };
+          if (mq.Remove(f))
+          {
+            if (element != counter)
+            {
+              std::ostringstream ss;
+              ss << "Counter mismatch. expected = " << counter << ". actual = " << element;
+              threadFailMessage = ss.str();
+              return;
+            }
+            noticeIsFresh = false;
+            ++counter; // counter represents the number of elements removed, so only increment when remove() returns true
+          }
+          else if (noticeIsFresh)
+          {
+            threadFailMessage = "Notice received, but no element to remove from queue.";
+            return;
+          }
+        }
+      }
+    }
+    catch (std::exception &e)
+    {
+      threadFailMessage = std::string("Exception: ") + e.what();
+    }
+    // End body of notice thread
+  });
+
+  // Body of test thread
+  for (unsigned int j = 1; j <= numberOfElements ; ++j)
+  {
+    mq.Insert(j);
+  };
+  th.join();
+  if (!threadFailMessage.empty())
+  {
+    FAIL() << threadFailMessage;
+  }
+  ASSERT_EQ(numberOfElements, counter);
+}
+
+/*
+ * All the ActiveQueue tests instantiante the ActiveQueue in a separate block.
+ * This ensures its internal thread has terminated before the end of the test,
+ *   and that the test will hang if it has not.
+ */
+
+TEST(ActiveQueue, Instance)
+{
+  bool seen = false;
+  auto process = [&](unsigned int) { seen = true; };
+  {
+    ActiveQueue<unsigned int, decltype(process)> aq(process);
+  }
+  ASSERT_FALSE(seen);
+}
+
+TEST(ActiveQueue, Single)
+{
+  bool seen = false;
+  auto process = [&](unsigned int) { seen = true; };
+  {
+    ActiveQueue<unsigned int, decltype(process)> aq(process);
+    aq.Insert(0);
+  }
+  ASSERT_TRUE(seen);
+}
+
+TEST(ActiveQueue, Multiple)
+{
+  static const unsigned int numberOfElements = 1000;
+  unsigned int counter = 0;
+  std::string threadFailMessage;
+  auto process = [&](unsigned int x) -> void {
+    ++counter;
+    if (x != counter)
+    {
+      std::ostringstream os;
+      os << "Mismatch: expected counter=" << counter << ", received queue element=" << x << ".";
+      threadFailMessage = os.str();
+    }
+  };
+  {
+    ActiveQueue<unsigned int, decltype(process)> aq(process);
+    for (unsigned int j = 1; j <= numberOfElements; ++j)
+    {
+      aq.Insert(j);
+      ASSERT_EQ("", threadFailMessage);
+    };
+  }
+  ASSERT_EQ(numberOfElements, counter);
+}
Index: test/plugin/PlaceholderTest.cpp
===================================================================
new file mode 100644
--- /dev/null
+++ b/test/plugin/PlaceholderTest.cpp
@@ -0,0 +1,147 @@
+/*
+ * This file is part of Adblock Plus <https://adblockplus.org/>,
+ * Copyright (C) 2006-2015 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <gtest/gtest.h>
+#include "../../src/plugin/ActiveQueue.h"
+#include <ostream>
+#include <memory>
+
+// The C compiler version for VS2012 is 17.00
+#if defined(_MSC_VER) && _MSC_VER <= 1700
+#define DELETED
+#else
+#define DELETED =delete
+#endif
+
+/**
+ * Class with only a copy constructor for an instantiation test.
+ */
+template<class T>
+struct MinimalCopy
+{
+  T value;
+
+  MinimalCopy() DELETED;
+  MinimalCopy(MinimalCopy&&) DELETED;
+  MinimalCopy& operator=(const MinimalCopy&) DELETED;
+  MinimalCopy& operator=(MinimalCopy&&) DELETED;
+
+  MinimalCopy(T x)
+    : value(x)
+  {}
+
+  MinimalCopy(const MinimalCopy& t)
+    : value(t.value)
+  {}
+};
+
+/**
+ * Class with only a move constructor for an instantiation test.
+ */
+template<class T>
+struct MinimalMove
+{
+  std::unique_ptr<int> a; // Not really needed, but illustrative
+  T value;
+
+  MinimalMove() DELETED;
+  MinimalMove(const MinimalMove&) DELETED;
+  MinimalMove& operator=(const MinimalMove&) DELETED;
+  MinimalMove& operator=(MinimalMove&&) DELETED;
+
+  MinimalMove(T x)
+    : a(nullptr), value(x)
+  {}
+
+  MinimalMove(MinimalMove&& x)
+    : a(std::move(x.a)), value(std::move(x.value))
+  {}
+};
+
+/*
+ * These are mostly a compilation tests.
+ * They check that the Placeholder only needs a single constructor, copy or move, to instantiate.
+ *
+ * For VS2012, using C++03 syntax, failure to instantiate the 'Placeholder' template 
+ *   will show up as a link error for one of declared-but-not-defined members.
+ * Using C++11 syntax, it will show up as a compile error.
+ *
+ * Extra blocks in test code illustrate the anticipated use of 'Placeholder'.
+ */
+
+/*
+ * Placeholder works with simple intrinsic types.
+ */
+TEST(Placeholder, Int)
+{
+  Placeholder<int> x;
+  {
+    x.Construct(42);
+  }
+  {
+    ASSERT_EQ(42, x.Object());
+  }
+}
+
+/*
+ * Placeholder works with only a copy constructor.
+ */
+TEST(Placeholder, MinimalCopy)
+{
+  typedef MinimalCopy<int> T;
+  Placeholder<T> x;
+  {
+    T a(42);
+    x.Construct(a); // Can't use a temporary because MinimalCopy has no move constructor (arbitrarily)
+  }
+  int y = 1;
+  {
+    y = x.Object().value;
+  }
+  ASSERT_EQ(42, y);
+}
+
+TEST(Placeholder, MinimalMove)
+{
+  typedef MinimalMove<int> T;
+  Placeholder<T> x;
+  {
+    x.Construct(std::move(T(42)));
+  }
+  int y = 1;
+  {
+    y = x.Object().value;
+  }
+  ASSERT_EQ(42, y);
+}
+
+TEST(Placeholder, MinimalMoveParameter)
+{
+  typedef MinimalMove<int> T;
+  Placeholder<T> x;
+  {
+    x.Construct(std::move(T(42)));
+  }
+  int y = 1;
+  {
+    y = [&](T&& t)
+    {
+      return t.value;
+    }(std::move(x.Object()));
+  }
+  ASSERT_EQ(42, y);
+}
+
