Bug 1580201 - Expose ContentBlockingLog to GV. r=snorp,ehsan a=RyanVM
authorDylan Roeh <droeh@mozilla.com>
Fri, 20 Sep 2019 14:17:37 -0500
changeset 555212 21feee30ec61a479885aa6b1f02f3e482b943635
parent 555211 909f6514015f84146ef257156a52c4b7452bd146
child 555213 33f9f9ae33627e26a2626b74e8d02822037a9792
push id2165
push userffxbld-merge
push dateMon, 14 Oct 2019 16:30:58 +0000
treeherdermozilla-release@0eae18af659f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp, ehsan, RyanVM
bugs1580201
milestone70.0
Bug 1580201 - Expose ContentBlockingLog to GV. r=snorp,ehsan a=RyanVM Differential Revision: https://phabricator.services.mozilla.com/D45350
mobile/android/chrome/geckoview/GeckoViewContentBlockingChild.js
mobile/android/chrome/geckoview/geckoview.js
mobile/android/geckoview/api.txt
mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ContentBlockingControllerTest.kt
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/ContentBlockingController.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md
mobile/android/modules/geckoview/GeckoViewContentBlocking.jsm
mobile/android/modules/geckoview/moz.build
uriloader/base/nsIWebProgressListener.idl
--- a/mobile/android/chrome/geckoview/GeckoViewContentBlockingChild.js
+++ b/mobile/android/chrome/geckoview/GeckoViewContentBlockingChild.js
@@ -15,16 +15,41 @@ class GeckoViewContentBlockingChild exte
     this.progressFilter = Cc[
       "@mozilla.org/appshell/component/browser-status-filter;1"
     ].createInstance(Ci.nsIWebProgress);
     this.progressFilter.addProgressListener(this, flags);
     const webProgress = docShell
       .QueryInterface(Ci.nsIInterfaceRequestor)
       .getInterface(Ci.nsIWebProgress);
     webProgress.addProgressListener(this.progressFilter, flags);
+
+    this.messageManager.addMessageListener("ContentBlocking:RequestLog", this);
+  }
+
+  receiveMessage(aMsg) {
+    debug`receiveMessage: ${aMsg.name}`;
+
+    switch (aMsg.name) {
+      case "ContentBlocking:RequestLog": {
+        docShell.getContentBlockingLog().then(
+          val =>
+            sendAsyncMessage("ContentBlocking:ExportLog", {
+              log: JSON.parse(val),
+              id: aMsg.data.id,
+            }),
+          reason =>
+            sendAsyncMessage("ContentBlocking:ExportLog", {
+              error: reason,
+              id: aMsg.data.id,
+            })
+        );
+
+        break;
+      }
+    }
   }
 
   onContentBlockingEvent(aWebProgress, aRequest, aEvent) {
     debug`onContentBlockingEvent ${aEvent.toString(16)}`;
 
     if (!aRequest || !(aRequest instanceof Ci.nsIClassifiedChannel)) {
       return;
     }
--- a/mobile/android/chrome/geckoview/geckoview.js
+++ b/mobile/android/chrome/geckoview/geckoview.js
@@ -518,16 +518,17 @@ function startup() {
       name: "GeckoViewTab",
       onInit: {
         resource: "resource://gre/modules/GeckoViewTab.jsm",
       },
     },
     {
       name: "GeckoViewContentBlocking",
       onEnable: {
+        resource: "resource://gre/modules/GeckoViewContentBlocking.jsm",
         frameScript:
           "chrome://geckoview/content/GeckoViewContentBlockingChild.js",
       },
     },
     {
       name: "SessionStateAggregator",
       onInit: {
         frameScript: "chrome://geckoview/content/SessionStateAggregator.js",
--- a/mobile/android/geckoview/api.txt
+++ b/mobile/android/geckoview/api.txt
@@ -213,28 +213,63 @@ package org.mozilla.geckoview {
     method @NonNull protected ContentBlocking.Settings newSettings(@Nullable ContentBlocking.Settings);
   }
 
   @AnyThread public class ContentBlockingController {
     ctor public ContentBlockingController();
     method @UiThread public void addException(@NonNull GeckoSession);
     method @UiThread @NonNull public GeckoResult<Boolean> checkException(@NonNull GeckoSession);
     method @UiThread public void clearExceptionList();
+    method @UiThread @NonNull public GeckoResult<List<ContentBlockingController.LogEntry>> getLog(@NonNull GeckoSession);
     method @UiThread public void removeException(@NonNull GeckoSession);
     method @UiThread public void restoreExceptionList(@NonNull ContentBlockingController.ExceptionList);
     method @UiThread @NonNull public GeckoResult<ContentBlockingController.ExceptionList> saveExceptionList();
   }
 
+  public static class ContentBlockingController.Event {
+    ctor protected Event();
+    field public static final int BLOCKED_CRYPTOMINING_CONTENT = 2048;
+    field public static final int BLOCKED_FINGERPRINTING_CONTENT = 64;
+    field public static final int BLOCKED_SOCIALTRACKING_CONTENT = 65536;
+    field public static final int BLOCKED_TRACKING_CONTENT = 4096;
+    field public static final int BLOCKED_UNSAFE_CONTENT = 16384;
+    field public static final int COOKIES_BLOCKED_ALL = 1073741824;
+    field public static final int COOKIES_BLOCKED_BY_PERMISSION = 268435456;
+    field public static final int COOKIES_BLOCKED_FOREIGN = 128;
+    field public static final int COOKIES_BLOCKED_TRACKER = 536870912;
+    field public static final int COOKIES_LOADED = 32768;
+    field public static final int COOKIES_LOADED_SOCIALTRACKER = 524288;
+    field public static final int COOKIES_LOADED_TRACKER = 262144;
+    field public static final int COOKIES_PARTITIONED_FOREIGN = -2147483648;
+    field public static final int LOADED_CRYPTOMINING_CONTENT = 2097152;
+    field public static final int LOADED_FINGERPRINTING_CONTENT = 1024;
+    field public static final int LOADED_SOCIALTRACKING_CONTENT = 131072;
+    field public static final int LOADED_TRACKING_CONTENT = 8192;
+  }
+
   @AnyThread public class ContentBlockingController.ExceptionList {
     ctor public ExceptionList(@NonNull String);
     ctor public ExceptionList(@NonNull JSONObject);
     method @NonNull public String[] getUris();
     method @NonNull public JSONObject toJson();
   }
 
+  @AnyThread public static class ContentBlockingController.LogEntry {
+    ctor protected LogEntry();
+    field @NonNull public final List<ContentBlockingController.LogEntry.BlockingData> blockingData;
+    field @NonNull public final String origin;
+  }
+
+  public static class ContentBlockingController.LogEntry.BlockingData {
+    ctor protected BlockingData();
+    field public final boolean blocked;
+    field public final int category;
+    field public final int count;
+  }
+
   public class CrashReporter {
     ctor public CrashReporter();
     method @AnyThread @NonNull public static GeckoResult<String> sendCrashReport(@NonNull Context, @NonNull Intent, @NonNull String);
     method @AnyThread @NonNull public static GeckoResult<String> sendCrashReport(@NonNull Context, @NonNull Bundle, @NonNull String);
     method @AnyThread @NonNull public static GeckoResult<String> sendCrashReport(@NonNull Context, @NonNull File, @NonNull File, @NonNull String);
     method @AnyThread @NonNull public static GeckoResult<String> sendCrashReport(@NonNull Context, @NonNull File, @NonNull Map<String,String>, @NonNull String);
   }
 
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ContentBlockingControllerTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ContentBlockingControllerTest.kt
@@ -133,9 +133,32 @@ class ContentBlockingControllerTest : Ba
         export = sessionRule.waitForResult(sessionRule.runtime.contentBlockingController
                 .saveExceptionList())
         assertThat("Exported list must not be null", export, Matchers.notNullValue())
         assertThat("Exported list must contain one entry", export.uris.size, Matchers.equalTo(1))
 
         // Wipe so as not to break other tests.
         sessionRule.runtime.contentBlockingController.clearExceptionList()
     }
+
+    @Test
+    fun getLog() {
+        val category = ContentBlocking.AntiTracking.TEST
+        sessionRule.runtime.settings.contentBlocking.setAntiTracking(category)
+        sessionRule.session.settings.useTrackingProtection = true
+        sessionRule.session.loadTestPath(TRACKERS_PATH)
+
+        sessionRule.waitForPageStop()
+
+        sessionRule.waitForResult(sessionRule.runtime.contentBlockingController.getLog(sessionRule.session).accept {
+            assertThat("Log must not be null", it, Matchers.notNullValue())
+            assertThat("Log must have at least one entry", it?.size, Matchers.not(0))
+            it?.forEach {
+                it.blockingData.forEach {
+                    assertThat("Category must match", it.category,
+                            Matchers.equalTo(ContentBlockingController.Event.BLOCKED_TRACKING_CONTENT))
+                    assertThat("Blocked must be true", it.blocked, Matchers.equalTo(true))
+                    assertThat("Count must be at least 1", it.count, Matchers.not(0))
+                }
+            }
+        })
+    }
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/ContentBlockingController.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/ContentBlockingController.java
@@ -8,21 +8,28 @@ package org.mozilla.geckoview;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.util.GeckoBundle;
 
 import android.support.annotation.AnyThread;
+import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 import android.support.annotation.NonNull;
 import android.support.annotation.UiThread;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
 /**
  * ContentBlockingController is used to manage and modify the content
  * blocking exception list. This list is shared across all sessions.
  */
 @AnyThread
 public class ContentBlockingController {
     private static final String LOGTAG = "GeckoContentBlocking";
 
@@ -190,9 +197,211 @@ public class ContentBlockingController {
 
     /**
      * Clear the content blocking exception list entirely.
      */
     @UiThread
     public void clearExceptionList() {
         EventDispatcher.getInstance().dispatch("ContentBlocking:ClearList", null);
     }
+
+    public static class Event {
+        // These values must be kept in sync with the corresponding values in
+        // nsIWebProgressListener.idl.
+        /**
+         * Tracking content has been blocked from loading.
+         */
+        public static final int BLOCKED_TRACKING_CONTENT        = 0x00001000;
+
+        /**
+         * Tracking content has been loaded.
+         */
+        public static final int LOADED_TRACKING_CONTENT         = 0x00002000;
+
+        /**
+         * Fingerprinting content has been blocked from loading.
+         */
+        public static final int BLOCKED_FINGERPRINTING_CONTENT  = 0x00000040;
+
+        /**
+         * Fingerprinting content has been loaded.
+         */
+        public static final int LOADED_FINGERPRINTING_CONTENT   = 0x00000400;
+
+        /**
+         * Cryptomining content has been blocked from loading.
+         */
+        public static final int BLOCKED_CRYPTOMINING_CONTENT    = 0x00000800;
+
+        /**
+         * Cryptomining content has been loaded.
+         */
+        public static final int LOADED_CRYPTOMINING_CONTENT     = 0x00200000;
+
+        /**
+         * Content which appears on the SafeBrowsing list has been blocked from loading.
+         */
+        public static final int BLOCKED_UNSAFE_CONTENT          = 0x00004000;
+
+        /**
+         * Performed a storage access check, which usually means something like a
+         * cookie or a storage item was loaded/stored on the current tab.
+         * Alternatively this could indicate that something in the current tab
+         * attempted to communicate with its same-origin counterparts in other
+         * tabs.
+         */
+        public static final int COOKIES_LOADED                  = 0x00008000;
+
+        /**
+         * Similar to {@link COOKIES_LOADED}, but only sent if the subject of the
+         * action was a third-party tracker when the active cookie policy imposes
+         * restrictions on such content.
+         */
+        public static final int COOKIES_LOADED_TRACKER          = 0x00040000;
+
+        /**
+         * Similar to {@link COOKIES_LOADED}, but only sent if the subject of the
+         * action was a third-party social tracker when the active cookie policy
+         * imposes restrictions on such content.
+         */
+        public static final int COOKIES_LOADED_SOCIALTRACKER    = 0x00080000;
+
+        /**
+         * Rejected for custom site permission.
+         */
+        public static final int COOKIES_BLOCKED_BY_PERMISSION   = 0x10000000;
+
+        /**
+         * Rejected because the resource is a tracker and cookie policy doesn't
+         * allow its loading.
+         */
+        public static final int COOKIES_BLOCKED_TRACKER         = 0x20000000;
+
+        /**
+         * Rejected because cookie policy blocks all cookies.
+         */
+        public static final int COOKIES_BLOCKED_ALL             = 0x40000000;
+
+        /**
+         * Rejected because the resource is a third-party and cookie policy forces
+         * third-party resources to be partitioned.
+         */
+        public static final int COOKIES_PARTITIONED_FOREIGN     = 0x80000000;
+
+        /**
+         * Rejected because cookie policy blocks 3rd party cookies.
+         */
+        public static final int COOKIES_BLOCKED_FOREIGN         = 0x00000080;
+
+        /**
+         * SocialTracking content has been blocked from loading.
+         */
+        public static final int BLOCKED_SOCIALTRACKING_CONTENT  = 0x00010000;
+
+        /**
+         * SocialTracking content has been loaded.
+         */
+        public static final int LOADED_SOCIALTRACKING_CONTENT   = 0x00020000;
+
+        protected Event() {}
+    }
+
+    /**
+     * An entry in the content blocking log for a site.
+     */
+    @AnyThread
+    public static class LogEntry {
+        /**
+         * Data about why a given entry was blocked.
+         */
+        public static class BlockingData {
+            @Retention(RetentionPolicy.SOURCE)
+            @IntDef({ Event.BLOCKED_TRACKING_CONTENT, Event.LOADED_TRACKING_CONTENT,
+                      Event.BLOCKED_FINGERPRINTING_CONTENT, Event.LOADED_FINGERPRINTING_CONTENT,
+                      Event.BLOCKED_CRYPTOMINING_CONTENT, Event.LOADED_CRYPTOMINING_CONTENT,
+                      Event.BLOCKED_UNSAFE_CONTENT, Event.COOKIES_LOADED,
+                      Event.COOKIES_LOADED_TRACKER, Event.COOKIES_LOADED_SOCIALTRACKER,
+                      Event.COOKIES_BLOCKED_BY_PERMISSION, Event.COOKIES_BLOCKED_TRACKER,
+                      Event.COOKIES_BLOCKED_ALL, Event.COOKIES_PARTITIONED_FOREIGN,
+                      Event.COOKIES_BLOCKED_FOREIGN, Event.BLOCKED_SOCIALTRACKING_CONTENT,
+                      Event.LOADED_SOCIALTRACKING_CONTENT })
+            /* package */ @interface LogEvent {}
+
+            /**
+             * A category the entry falls under.
+             */
+            public final @LogEvent int category;
+
+            /**
+             * Indicates whether or not blocking occured for this category,
+             * where applicable.
+             */
+            public final boolean blocked;
+
+            /**
+             * The count of consecutive repeated appearances.
+             */
+            public final int count;
+
+            /* package */ BlockingData(final @NonNull GeckoBundle bundle) {
+                category = bundle.getInt("category");
+                blocked = bundle.getBoolean("blocked");
+                count = bundle.getInt("count");
+            }
+
+            protected BlockingData() {
+                category = 0;
+                blocked = false;
+                count = 0;
+            }
+        }
+
+        /**
+         * The origin of this log entry.
+         */
+        public final @NonNull String origin;
+
+        /**
+         * The blocking data for this origin, sorted chronologically.
+         */
+        public final @NonNull List<BlockingData> blockingData;
+
+        /* package */ LogEntry(final @NonNull GeckoBundle bundle) {
+            origin = bundle.getString("origin");
+            final GeckoBundle[] data = bundle.getBundleArray("blockData");
+            final ArrayList<BlockingData> dataArray = new ArrayList<BlockingData>(data.length);
+            for (GeckoBundle b : data) {
+                dataArray.add(new BlockingData(b));
+            }
+            blockingData = Collections.unmodifiableList(dataArray);
+        }
+
+        protected LogEntry() {
+            origin = null;
+            blockingData = null;
+        }
+    }
+
+    /**
+     * Get a log of all content blocking information for the site currently loaded by the
+     * supplied {@link GeckoSession}.
+     *
+     * @param session A {@link GeckoSession} for which you want the content blocking log.
+     *
+     * @return A {@link GeckoResult} that resolves to the list of content blocking log entries.
+     */
+    @UiThread
+    public @NonNull GeckoResult<List<LogEntry>> getLog(final @NonNull GeckoSession session) {
+        final CallbackResult<List<LogEntry>> result = new CallbackResult<List<LogEntry>>() {
+            @Override
+            public void sendSuccess(final Object value) {
+                final GeckoBundle[] bundles = ((GeckoBundle) value).getBundleArray("log");
+                final ArrayList<LogEntry> logArray = new ArrayList<LogEntry>(bundles.length);
+                for (GeckoBundle b : bundles) {
+                    logArray.add(new LogEntry(b));
+                }
+                complete(Collections.unmodifiableList(logArray));
+            }
+        };
+        session.getEventDispatcher().dispatch("ContentBlocking:RequestLog", null, result);
+        return result;
+    }
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md
@@ -53,16 +53,18 @@ exclude: true
   ([bug 1539144]({{bugzilla}}1539144))
 - Added [`onCloseTab`][70.21] to [`WebExtensionController.TabDelegate`][70.19] to handle
   [`browser.tabs.remove`][70.22] calls by WebExtensions.
   ([bug 1565782]({{bugzilla}}1565782))
 - Added onSlowScript to [`ContentDelegate`][70.23] which allows handling of slow and hung scripts.
   ([bug 1621094]({{bugzilla}}1621094))
 - Added [`ContentBlockingController`][70.24], accessible via [`GeckoRuntime.getContentBlockingController`][70.25]
   to allow modification and inspection of a content blocking exception list.
+- Added support for exposing the content blocking log in [`ContentBlockingController`][70.26].
+  ([bug 1580201]({{bugzilla}}1580201))
 
 [70.1]: {{javadoc_uri}}/GeckoSessionSettings.Builder.html#contextId-java.lang.String-
 [70.2]: {{javadoc_uri}}/StorageController.html#clearDataForSessionContext-java.lang.String-
 [70.3]: {{javadoc_uri}}/CrashReporter.html#sendCrashReport-android.content.Context-java.io.File-java.io.File-java.lang.String-
 [70.4]: {{javadoc_uri}}/CrashReporter.html#sendCrashReport-android.content.Context-java.io.File-java.util.Map-java.lang.String-
 [70.5]: {{javadoc_uri}}/GeckoView.html
 [70.6]: {{javadoc_uri}}/GeckoSession.html
 [70.7]: {{javadoc_uri}}/GeckoSession.PromptDelegate.html#CAPTURE_TYPE_NONE
@@ -79,16 +81,17 @@ exclude: true
 [70.18]: {{javadoc_uri}}/WebExtensionController.html
 [70.19]: {{javadoc_uri}}/WebExtensionController.TabDelegate.html
 [70.20]: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/create
 [70.21]: {{javadoc_uri}}/WebExtensionController.TabDelegate.html#onCloseTab-org.mozilla.geckoview.WebExtension-org.mozilla.geckoview.GeckoSession-
 [70.22]: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/remove
 [70.23]: {{javadoc_uri}}/GeckoSession.ContentDelegate.html
 [70.24]: {{javadoc_uri}}/ContentBlockingController.html
 [70.25]: {{javadoc_uri}}/GeckoRuntime.html#getContentBlockingController--
+[70.26]: {{javadoc_uri}}/ContentBlockingController.html
 
 ## v69
 - Modified behavior of ['setAutomaticFontSizeAdjustment'][69.1] so that it no 
   longer has any effect on ['setFontInflationEnabled'][69.2]
 - Add [GeckoSession.LOAD_FLAGS_FORCE_ALLOW_DATA_URI][69.14]
 - Added [`GeckoResult.accept`][69.3] for consuming a result without
   transforming it.
 - [`GeckoSession.setMessageDelegate`][69.13] callers must now specify the
@@ -322,9 +325,9 @@ exclude: true
 [65.19]: {{javadoc_uri}}/GeckoSession.NavigationDelegate.LoadRequest.html#isRedirect
 [65.20]: {{javadoc_uri}}/GeckoSession.html#LOAD_FLAGS_BYPASS_CLASSIFIER    
 [65.21]: {{javadoc_uri}}/GeckoSession.ContentDelegate.ContextElement.html
 [65.22]: {{javadoc_uri}}/GeckoSession.ContentDelegate.html#onContextMenu-org.mozilla.geckoview.GeckoSession-int-int-org.mozilla.geckoview.GeckoSession.ContentDelegate.ContextElement-
 [65.23]: {{javadoc_uri}}/GeckoSession.FinderResult.html
 [65.24]: {{javadoc_uri}}/CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String-
 [65.25]: {{javadoc_uri}}/GeckoResult.html
 
-[api-version]: f2d89facd57470b760be9a867173d02bf9040c75
+[api-version]: c51e338dbc7220c0fe2676826b36e1bb346d58bc
new file mode 100644
--- /dev/null
+++ b/mobile/android/modules/geckoview/GeckoViewContentBlocking.jsm
@@ -0,0 +1,97 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+var EXPORTED_SYMBOLS = ["GeckoViewContentBlocking"];
+
+const { GeckoViewModule } = ChromeUtils.import(
+  "resource://gre/modules/GeckoViewModule.jsm"
+);
+
+class GeckoViewContentBlocking extends GeckoViewModule {
+  onEnable() {
+    this.registerListener(["ContentBlocking:RequestLog"]);
+
+    this.messageManager.addMessageListener("ContentBlocking:ExportLog", this);
+  }
+
+  onDisable() {
+    this.unregisterListener(["ContentBlocking:RequestLog"]);
+
+    this.messageManager.removeMessageListener(
+      "ContentBlocking:ExportLog",
+      this
+    );
+  }
+
+  // Bundle event handler.
+  onEvent(aEvent, aData, aCallback) {
+    debug`onEvent: event=${aEvent}, data=${aData}`;
+
+    switch (aEvent) {
+      case "ContentBlocking:RequestLog": {
+        if (!this._requestLogCallbacks) {
+          this._requestLogCallbacks = new Map();
+          this._requestLogId = 0;
+        }
+        this._requestLogCallbacks.set(this._requestLogId, aCallback);
+        this.messageManager.sendAsyncMessage("ContentBlocking:RequestLog", {
+          id: this._requestLogId,
+        });
+        this._requestLogId++;
+        break;
+      }
+    }
+  }
+
+  // Message manager event handler.
+  receiveMessage(aMsg) {
+    debug`receiveMessage: ${aMsg.name}`;
+
+    switch (aMsg.name) {
+      case "ContentBlocking:ExportLog": {
+        if (
+          !this._requestLogCallbacks ||
+          !this._requestLogCallbacks.has(aMsg.data.id)
+        ) {
+          warn`Failed to export content blocking log.`;
+          return;
+        }
+
+        const callback = this._requestLogCallbacks.get(aMsg.data.id);
+
+        if (!aMsg.data.log) {
+          warn`Failed to export content blocking log.`;
+          callback.onError(aMsg.data.error);
+          // Clean up the callback even on a failed response.
+          this._requestLogCallbacks.delete(aMsg.data.id);
+          return;
+        }
+
+        const res = Object.keys(aMsg.data.log).map(key => {
+          const blockData = aMsg.data.log[key].map(data => {
+            return {
+              category: data[0],
+              blocked: data[1],
+              count: data[2],
+            };
+          });
+          return {
+            origin: key,
+            blockData: blockData,
+          };
+        });
+        callback.onSuccess({ log: res });
+
+        this._requestLogCallbacks.delete(aMsg.data.id);
+        break;
+      }
+    }
+  }
+}
+
+const { debug, warn } = GeckoViewContentBlocking.initLogging(
+  "GeckoViewContentBlocking"
+); // eslint-disable-line no-unused-vars
--- a/mobile/android/modules/geckoview/moz.build
+++ b/mobile/android/modules/geckoview/moz.build
@@ -8,16 +8,17 @@ EXTRA_JS_MODULES += [
     'AndroidLog.jsm',
     'ContentCrashHandler.jsm',
     'DelayedInit.jsm',
     'GeckoViewAccessibility.jsm',
     'GeckoViewAutoFill.jsm',
     'GeckoViewChildModule.jsm',
     'GeckoViewConsole.jsm',
     'GeckoViewContent.jsm',
+    'GeckoViewContentBlocking.jsm',
     'GeckoViewContentBlockingController.jsm',
     'GeckoViewMedia.jsm',
     'GeckoViewModule.jsm',
     'GeckoViewNavigation.jsm',
     'GeckoViewProcessHangMonitor.jsm',
     'GeckoViewProgress.jsm',
     'GeckoViewRemoteDebugger.jsm',
     'GeckoViewSettings.jsm',
--- a/uriloader/base/nsIWebProgressListener.idl
+++ b/uriloader/base/nsIWebProgressListener.idl
@@ -255,17 +255,18 @@ interface nsIWebProgressListener : nsISu
   const unsigned long STATE_USES_SSL_3                = 0x01000000;
   const unsigned long STATE_USES_WEAK_CRYPTO          = 0x02000000;
   const unsigned long STATE_CERT_USER_OVERRIDDEN      = 0x04000000;
 
   /**
    * Content Blocking Event flags
    *
    * NOTE: IF YOU ARE ADDING MORE OF THESE FLAGS, MAKE SURE TO EDIT
-   * nsSecureBrowserUIImpl::CheckForBlockedContent().
+   * nsSecureBrowserUIImpl::CheckForBlockedContent() AND UPDATE THE
+   * CORRESPONDING LIST IN ContentBlockingController.java
    *
    * These flags describe the reason of cookie jar rejection.
    *
    * STATE_BLOCKED_TRACKING_CONTENT
    *   Tracking content has been blocked from loading.
    *
    * STATE_LOADED_TRACKING_CONTENT
    *   Tracking content has been loaded.