Index: lib/common.js
===================================================================
--- a/lib/common.js
+++ b/lib/common.js
@@ -96,8 +96,110 @@
     }
   }
 
   selectors.push(selector.substring(start));
   return selectors;
 }
 
 exports.splitSelector = splitSelector;
+
+function findTargetSelectorIndex(selector)
+{
+  let index = 0;
+  let whitespace = 0;
+  let scope = [];
+
+  // Start from the end of the string and go character by character, where each
+  // character is a Unicode code point.
+  for (let character of [...selector].reverse())
+  {
+    let currentScope = scope[scope.length - 1];
+
+    if (character == "'" || character == "\"")
+    {
+      // If we're already within the same type of quote, close the scope;
+      // otherwise open a new scope.
+      if (currentScope == character)
+        scope.pop();
+      else
+        scope.push(character);
+    }
+    else if (character == "]" || character == ")")
+    {
+      // For closing brackets and parentheses, open a new scope only if we're
+      // not within a quote. Within quotes these characters should have no
+      // meaning.
+      if (currentScope != "'" && currentScope != "\"")
+        scope.push(character);
+    }
+    else if (character == "[")
+    {
+      // If we're already within a bracket, close the scope.
+      if (currentScope == "]")
+        scope.pop();
+    }
+    else if (character == "(")
+    {
+      // If we're already within a parenthesis, close the scope.
+      if (currentScope == ")")
+        scope.pop();
+    }
+    else if (!currentScope)
+    {
+      // At the top level (not within any scope), count the whitespace if we've
+      // encountered it. Otherwise if we've hit one of the combinators,
+      // terminate here; otherwise if we've hit a non-colon character,
+      // terminate here.
+      if (/\s/u.test(character))
+      {
+        whitespace++;
+      }
+      else if ((character == ">" || character == "+" || character == "~") ||
+               (whitespace > 0 && character != ":"))
+      {
+        break;
+      }
+    }
+
+    // Zero out the whitespace count if we've entered a scope.
+    if (scope.length > 0)
+      whitespace = 0;
+
+    // Increment the index by the size of the character. Note that for Unicode
+    // composite characters (like emoji) this will be more than one.
+    index += character.length;
+  }
+
+  return selector.length - index + whitespace;
+}
+
+/**
+ * Qualifies a CSS selector with a qualifier, which may be another CSS selector
+ * or an empty string. For example, given the selector "div.bar" and the
+ * qualifier "#foo", this function returns "div#foo.bar".
+ * @param {string} selector The selector to qualify.
+ * @param {string} qualifier The qualifier with which to qualify the selector.
+ * @returns {string} The qualified selector.
+ */
+function qualifySelector(selector, qualifier)
+{
+  let qualifiedSelector = "";
+
+  for (let sub of splitSelector(selector))
+  {
+    sub = sub.trim();
+
+    qualifiedSelector += ", ";
+
+    let index = findTargetSelectorIndex(sub);
+    let [, type = "", rest] = /^([a-z][a-z-]*)?(.*)/i.exec(sub.substr(index));
+
+    // Note that the first group in the regular expression is optional. If it
+    // doesn't match (e.g. "#foo::nth-child(1)"), type will be an empty string.
+    qualifiedSelector += sub.substr(0, index) + type + qualifier + rest;
+  }
+
+  // Remove the initial comma and space.
+  return qualifiedSelector.substr(2);
+}
+
+exports.qualifySelector = qualifySelector;
Index: lib/content/elemHideEmulation.js
===================================================================
--- a/lib/content/elemHideEmulation.js
+++ b/lib/content/elemHideEmulation.js
@@ -12,17 +12,18 @@
  * 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/>.
  */
 
 "use strict";
 
-const {textToRegExp, filterToRegExp, splitSelector} = require("../common");
+const {textToRegExp, filterToRegExp, splitSelector,
+       qualifySelector} = require("../common");
 const {indexOf} = require("../coreUtils");
 
 let MIN_INVOCATION_INTERVAL = 3000;
 const MAX_SYNCHRONOUS_PROCESSING_TIME = 50;
 const abpSelectorRegexp = /:-abp-([\w-]+)\(/i;
 
 function getCachedPropertyValue(object, name, defaultValueFunc = () => {})
 {
@@ -379,17 +380,17 @@
           if (subSelector.startsWith("*") &&
               !incompletePrefixRegexp.test(prefix))
           {
             subSelector = subSelector.substr(1);
           }
           let idx = subSelector.lastIndexOf("::");
           if (idx != -1)
             subSelector = subSelector.substr(0, idx);
-          yield prefix + subSelector;
+          yield qualifySelector(subSelector, prefix);
         }
   },
 
   *getSelectors(prefix, subtree, styles)
   {
     for (let selector of this.findPropsSelectors(styles, prefix, this._regexp))
       yield [selector, subtree];
   }
Index: test/common.js
===================================================================
new file mode 100644
--- /dev/null
+++ b/test/common.js
@@ -0,0 +1,188 @@
+/*
+ * This file is part of Adblock Plus <https://adblockplus.org/>,
+ * Copyright (C) 2006-present 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/>.
+ */
+
+"use strict";
+
+const {createSandbox} = require("./_common");
+
+let qualifySelector = null;
+
+exports.setUp = function(callback)
+{
+  let sandboxedRequire = createSandbox();
+  (
+    {qualifySelector} = sandboxedRequire("../lib/common")
+  );
+  callback();
+};
+
+exports.testQualifySelector = function(test)
+{
+  // Simple selectors.
+  test.equal(qualifySelector("#foo", "div"), "div#foo");
+  test.equal(qualifySelector(".foo", "div"), "div.foo");
+  test.equal(qualifySelector("div", "#foo"), "div#foo");
+  test.equal(qualifySelector("div", ".foo"), "div.foo");
+  test.equal(qualifySelector("div#bar", ".foo"), "div.foo#bar");
+  test.equal(qualifySelector("div.bar", "#foo"), "div#foo.bar");
+
+  // Compound selectors.
+  test.equal(qualifySelector("body #foo", "div"), "body div#foo");
+  test.equal(qualifySelector("body .foo", "div"), "body div.foo");
+  test.equal(qualifySelector("body div", "#foo"), "body div#foo");
+  test.equal(qualifySelector("body div", ".foo"), "body div.foo");
+  test.equal(qualifySelector("body div#bar", ".foo"), "body div.foo#bar");
+  test.equal(qualifySelector("body div.bar", "#foo"), "body div#foo.bar");
+
+  // Compound selectors with pseudo-class with parentheses.
+  test.equal(qualifySelector("body #foo:nth-child(1)", "div"),
+             "body div#foo:nth-child(1)");
+  test.equal(qualifySelector("body .foo:nth-child(1)", "div"),
+             "body div.foo:nth-child(1)");
+  test.equal(qualifySelector("body div:nth-child(1)", "#foo"),
+             "body div#foo:nth-child(1)");
+  test.equal(qualifySelector("body div:nth-child(1)", ".foo"),
+             "body div.foo:nth-child(1)");
+  test.equal(qualifySelector("body div#bar:nth-child(1)", ".foo"),
+             "body div.foo#bar:nth-child(1)");
+  test.equal(qualifySelector("body div.bar:nth-child(1)", "#foo"),
+             "body div#foo.bar:nth-child(1)");
+
+  // Compound selectors with pseudo-class with parentheses containing extra
+  // whitespace.
+  test.equal(qualifySelector("body #foo:nth-child( 1 )", "div"),
+             "body div#foo:nth-child( 1 )");
+  test.equal(qualifySelector("body .foo:nth-child( 1 )", "div"),
+             "body div.foo:nth-child( 1 )");
+  test.equal(qualifySelector("body div:nth-child( 1 )", "#foo"),
+             "body div#foo:nth-child( 1 )");
+  test.equal(qualifySelector("body div:nth-child( 1 )", ".foo"),
+             "body div.foo:nth-child( 1 )");
+  test.equal(qualifySelector("body div#bar:nth-child( 1 )", ".foo"),
+             "body div.foo#bar:nth-child( 1 )");
+  test.equal(qualifySelector("body div.bar:nth-child( 1 )", "#foo"),
+             "body div#foo.bar:nth-child( 1 )");
+
+  // Compound selectors with child combinator and pseudo-class with
+  // parentheses.
+  test.equal(qualifySelector("body > #foo:nth-child(1)", "div"),
+             "body > div#foo:nth-child(1)");
+  test.equal(qualifySelector("body > .foo:nth-child(1)", "div"),
+             "body > div.foo:nth-child(1)");
+  test.equal(qualifySelector("body > div:nth-child(1)", "#foo"),
+             "body > div#foo:nth-child(1)");
+  test.equal(qualifySelector("body > div:nth-child(1)", ".foo"),
+             "body > div.foo:nth-child(1)");
+  test.equal(qualifySelector("body > div#bar:nth-child(1)", ".foo"),
+             "body > div.foo#bar:nth-child(1)");
+  test.equal(qualifySelector("body > div.bar:nth-child(1)", "#foo"),
+             "body > div#foo.bar:nth-child(1)");
+
+  // Compound selectors with child combinator surrounded by no whitespace and
+  // pseudo-class with parentheses.
+  test.equal(qualifySelector("body>#foo:nth-child(1)", "div"),
+             "body>div#foo:nth-child(1)");
+  test.equal(qualifySelector("body>.foo:nth-child(1)", "div"),
+             "body>div.foo:nth-child(1)");
+  test.equal(qualifySelector("body>div:nth-child(1)", "#foo"),
+             "body>div#foo:nth-child(1)");
+  test.equal(qualifySelector("body>div:nth-child(1)", ".foo"),
+             "body>div.foo:nth-child(1)");
+  test.equal(qualifySelector("body>div#bar:nth-child(1)", ".foo"),
+             "body>div.foo#bar:nth-child(1)");
+  test.equal(qualifySelector("body>div.bar:nth-child(1)", "#foo"),
+             "body>div#foo.bar:nth-child(1)");
+
+  // Compound selectors with adjacent sibling combinator and pseudo-class with
+  // parentheses.
+  test.equal(qualifySelector("article + #foo:nth-child(1)", "div"),
+             "article + div#foo:nth-child(1)");
+  test.equal(qualifySelector("article + .foo:nth-child(1)", "div"),
+             "article + div.foo:nth-child(1)");
+  test.equal(qualifySelector("article + div:nth-child(1)", "#foo"),
+             "article + div#foo:nth-child(1)");
+  test.equal(qualifySelector("article + div:nth-child(1)", ".foo"),
+             "article + div.foo:nth-child(1)");
+  test.equal(qualifySelector("article + div#bar:nth-child(1)", ".foo"),
+             "article + div.foo#bar:nth-child(1)");
+  test.equal(qualifySelector("article + div.bar:nth-child(1)", "#foo"),
+             "article + div#foo.bar:nth-child(1)");
+
+  // Compound selectors with general sibling combinator and pseudo-class with
+  // parentheses.
+  test.equal(qualifySelector("article ~ #foo:nth-child(1)", "div"),
+             "article ~ div#foo:nth-child(1)");
+  test.equal(qualifySelector("article ~ .foo:nth-child(1)", "div"),
+             "article ~ div.foo:nth-child(1)");
+  test.equal(qualifySelector("article ~ div:nth-child(1)", "#foo"),
+             "article ~ div#foo:nth-child(1)");
+  test.equal(qualifySelector("article ~ div:nth-child(1)", ".foo"),
+             "article ~ div.foo:nth-child(1)");
+  test.equal(qualifySelector("article ~ div#bar:nth-child(1)", ".foo"),
+             "article ~ div.foo#bar:nth-child(1)");
+  test.equal(qualifySelector("article ~ div.bar:nth-child(1)", "#foo"),
+             "article ~ div#foo.bar:nth-child(1)");
+
+  // Compound selectors with child combinator and pseudo-element.
+  test.equal(qualifySelector("body > #foo::first-child", "div"),
+             "body > div#foo::first-child");
+  test.equal(qualifySelector("body > .foo::first-child", "div"),
+             "body > div.foo::first-child");
+  test.equal(qualifySelector("body > div::first-child", "#foo"),
+             "body > div#foo::first-child");
+  test.equal(qualifySelector("body > div::first-child", ".foo"),
+             "body > div.foo::first-child");
+  test.equal(qualifySelector("body > div#bar::first-child", ".foo"),
+             "body > div.foo#bar::first-child");
+  test.equal(qualifySelector("body > div.bar::first-child", "#foo"),
+             "body > div#foo.bar::first-child");
+
+  // Compound selectors with attribute selector.
+  test.equal(qualifySelector("body #foo[style='display: block']", "div"),
+             "body div#foo[style='display: block']");
+  test.equal(qualifySelector("body .foo[style='display: block']", "div"),
+             "body div.foo[style='display: block']");
+  test.equal(qualifySelector("body div[style='display: block']", "#foo"),
+             "body div#foo[style='display: block']");
+  test.equal(qualifySelector("body div[style='display: block']", ".foo"),
+             "body div.foo[style='display: block']");
+  test.equal(qualifySelector("body div#bar[style='display: block']", ".foo"),
+             "body div.foo#bar[style='display: block']");
+  test.equal(qualifySelector("body div.bar[style='display: block']", "#foo"),
+             "body div#foo.bar[style='display: block']");
+
+  // Compound selectors with unqualified attribute selector.
+  test.equal(qualifySelector("body [style='display: block']", "div"),
+             "body div[style='display: block']");
+  test.equal(qualifySelector("body [style='display: block']", "#foo"),
+             "body #foo[style='display: block']");
+  test.equal(qualifySelector("body [style='display: block']", ".foo"),
+             "body .foo[style='display: block']");
+
+  // Multiple selectors.
+  test.equal(qualifySelector("#foo, #bar", "div"), "div#foo, div#bar");
+  test.equal(qualifySelector(".foo, .bar", "div"), "div.foo, div.bar");
+  test.equal(qualifySelector("div, .bar", "#foo"), "div#foo, #foo.bar");
+  test.equal(qualifySelector("div, #bar", ".foo"), "div.foo, .foo#bar");
+
+  // Compound selector with class selector containing Unicode composite
+  // character.
+  test.equal(qualifySelector("body .\ud83d\ude42", "img"),
+             "body img.\ud83d\ude42");
+
+  test.done();
+};
