Index: lib/content/snippets.js
===================================================================
--- a/lib/content/snippets.js
+++ b/lib/content/snippets.js
@@ -10,18 +10,118 @@
  * 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/>.
  */
 
+/* eslint-env webextensions */
 /* eslint no-console: "off" */
 
 "use strict";
 
+/**
+ * Injects JavaScript code into the document using a temporary
+ * <code>script</code> element.
+ *
+ * @param {string} code The code to inject.
+ * @param {Array.<function|string>} [dependencies] A list of dependencies
+ *   to inject along with the code. A dependency may be either a function or a
+ *   string containing some executable code.
+ */
+function injectCode(code, dependencies = [])
+{
+  for (let dependency of dependencies)
+    code += dependency;
+
+  let script = document.createElement("script");
+
+  script.type = "application/javascript";
+  script.async = false;
+
+  // Firefox 58 only bypasses site CSPs when assigning to 'src',
+  // while Chrome 67 only bypasses site CSPs when using 'textContent'.
+  if (browser.runtime.getURL("").startsWith("chrome-extension://"))
+  {
+    script.textContent = code;
+    document.documentElement.appendChild(script);
+  }
+  else
+  {
+    let url = URL.createObjectURL(new Blob([code]));
+    script.src = url;
+    document.documentElement.appendChild(script);
+    URL.revokeObjectURL(url);
+  }
+
+  document.documentElement.removeChild(script);
+}
+
+/**
+ * Converts a function and an optional list of arguments into a string of code
+ * containing a function call. The function is converted to its string
+ * representation using the <code>Function.prototype.toString</code> method.
+ * Each argument is stringified using <code>JSON.stringify</code>. The
+ * generated code begins with the <code>"use strict"</code> directive.
+ *
+ * @param {function} func The function to convert.
+ * @param {...*} [params] The arguments to convert.
+ *
+ * @returns {string} The generated code containing the function call.
+ */
+function stringifyFunctionCall(func, ...params)
+{
+  // Call JSON.stringify on the arguments to avoid any arbitrary code
+  // execution.
+  return `"use strict";(${func})(${params.map(JSON.stringify).join(",")});`;
+}
+
+/**
+ * Wraps a function and its dependencies into an injector. The injector, when
+ * called with zero or more arguments, generates code that calls the function,
+ * with the given arguments, if any, and injects the code, along with any
+ * dependencies, into the document using a temporary <code>script</code>
+ * element.
+ *
+ * @param {function} injectable The function to wrap into an injector.
+ * @param {...(function|string)} [dependencies] Any dependencies of the
+ *   function. A dependency may be either a function or a string containing
+ *   some executable code.
+ *
+ * @returns {function} The generated injector.
+ */
+function makeInjector(injectable, ...dependencies)
+{
+  return (...args) => injectCode(stringifyFunctionCall(injectable, ...args),
+                                 dependencies);
+}
+
+/**
+ * Logs its arguments to the console. This may be used for testing and
+ * debugging.
+ *
+ * @param {...*} [args] The arguments to log.
+ */
 function log(...args)
 {
   console.log(...args);
 }
 
 exports.log = log;
+
+/**
+ * Similar to {@link log}, but does the logging in the context of the document
+ * rather than the content script. This may be used for testing and debugging,
+ * especially to verify that the injection of snippets into the document is
+ * working without any errors.
+ *
+ * @param {...*} [args] The arguments to log.
+ */
+function trace(...args)
+{
+  // We could simply use console.log here, but the goal is to demonstrate the
+  // usage of snippet dependencies.
+  log(...args);
+}
+
+exports.trace = makeInjector(trace, log);
