Bug 1548177 support incognito flag in request filtering r=kmag,robwu
authorShane Caraveo <scaraveo@mozilla.com>
Thu, 02 May 2019 16:23:03 +0000
changeset 531167 634737ac37d4c6bcc1a93206a144c5e58fa70aa4
parent 531166 596926f7e8125bcd9094a1183b2e887b592ae418
child 531168 8a834fa8a29f76d0a0268db1353d3d556fd2cf56
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskmag, robwu
bugs1548177
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1548177 support incognito flag in request filtering r=kmag,robwu Differential Revision: https://phabricator.services.mozilla.com/D29446
dom/chrome-webidl/ChannelWrapper.webidl
toolkit/components/extensions/ProxyScriptContext.jsm
toolkit/components/extensions/parent/ext-webRequest.js
toolkit/components/extensions/schemas/web_request.json
toolkit/components/extensions/test/xpcshell/test_ext_webRequest_incognito.js
toolkit/components/extensions/test/xpcshell/test_proxy_incognito.js
toolkit/components/extensions/webrequest/ChannelWrapper.cpp
toolkit/modules/addons/WebRequest.jsm
--- a/dom/chrome-webidl/ChannelWrapper.webidl
+++ b/dom/chrome-webidl/ChannelWrapper.webidl
@@ -457,16 +457,22 @@ dictionary MozRequestFilter {
    */
   sequence<MozContentPolicyType>? types = null;
 
   /**
    * If present, the request only matches if its finalURI matches the given
    * match pattern set.
    */
   MatchPatternSet? urls = null;
+
+  /**
+   * If present, the request only matches if the loadInfo privateBrowsingId matches
+   * against the given incognito value.
+   */
+  boolean? incognito = null;
 };
 
 dictionary MozRequestMatchOptions {
   /**
    * True if we're matching for the proxy portion of a proxied request.
    */
   boolean isProxy = false;
 };
--- a/toolkit/components/extensions/ProxyScriptContext.jsm
+++ b/toolkit/components/extensions/ProxyScriptContext.jsm
@@ -237,17 +237,18 @@ const ProxyInfoData = {
   },
 };
 
 function normalizeFilter(filter) {
   if (!filter) {
     filter = {};
   }
 
-  return {urls: filter.urls || null, types: filter.types || null};
+  return {urls: filter.urls || null, types: filter.types || null,
+          incognito: filter.incognito !== undefined ? filter.incognito : null};
 }
 
 class ProxyChannelFilter {
   constructor(context, extension, listener, filter, extraInfoSpec) {
     this.context = context;
     this.extension = extension;
     this.filter = normalizeFilter(filter);
     this.listener = listener;
--- a/toolkit/components/extensions/parent/ext-webRequest.js
+++ b/toolkit/components/extensions/parent/ext-webRequest.js
@@ -59,16 +59,19 @@ function registerEvent(extension, eventN
     filter2.types = filter.types;
   }
   if (filter.tabId) {
     filter2.tabId = filter.tabId;
   }
   if (filter.windowId) {
     filter2.windowId = filter.windowId;
   }
+  if (filter.incognito !== undefined) {
+    filter2.incognito = filter.incognito;
+  }
 
   let blockingAllowed = extension.hasPermission("webRequestBlocking");
 
   let info2 = [];
   if (info) {
     for (let desc of info) {
       if (desc == "blocking" && !blockingAllowed) {
         // This is usually checked in the child process (based on the API schemas, where these options
--- a/toolkit/components/extensions/schemas/web_request.json
+++ b/toolkit/components/extensions/schemas/web_request.json
@@ -114,17 +114,18 @@
           "types": {
             "type": "array",
             "optional": true,
             "description": "A list of request types. Requests that cannot match any of the types will be filtered out.",
             "items": { "$ref": "ResourceType" },
             "minItems": 1
           },
           "tabId": { "type": "integer", "optional": true },
-          "windowId": { "type": "integer", "optional": true }
+          "windowId": { "type": "integer", "optional": true },
+          "incognito": { "type": "boolean", "optional": true, "description": "If provided, requests that do not match the incognito state will be filtered out."}
         }
       },
       {
         "id": "HttpHeaders",
         "type": "array",
         "description": "An array of HTTP headers. Each header is represented as a dictionary containing the keys <code>name</code> and either <code>value</code> or <code>binaryValue</code>.",
         "items": {
           "type": "object",
--- a/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_incognito.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_incognito.js
@@ -14,18 +14,22 @@ add_task(async function test_incognito_w
   let pb_extension = ExtensionTestUtils.loadExtension({
     incognitoOverride: "spanning",
     manifest: {
       permissions: ["webRequest", "webRequestBlocking", "<all_urls>"],
     },
     background() {
       browser.webRequest.onBeforeRequest.addListener(async (details) => {
         browser.test.assertTrue(details.incognito, "incognito flag is set");
-        browser.test.notifyPass("webRequest.private");
-      }, {urls: ["<all_urls>"]}, ["blocking"]);
+      }, {urls: ["<all_urls>"], incognito: true}, ["blocking"]);
+
+      browser.webRequest.onBeforeRequest.addListener(async (details) => {
+        browser.test.assertFalse(details.incognito, "incognito flag is not set");
+        browser.test.notifyPass("webRequest.spanning");
+      }, {urls: ["<all_urls>"], incognito: false}, ["blocking"]);
     },
   });
   await pb_extension.startup();
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       permissions: ["webRequest", "webRequestBlocking", "<all_urls>"],
     },
@@ -35,19 +39,20 @@ add_task(async function test_incognito_w
         browser.test.notifyPass("webRequest");
       }, {urls: ["<all_urls>"]}, ["blocking"]);
     },
   });
   // Load non-incognito extension to check that private requests are invisible to it.
   await extension.startup();
 
   let contentPage = await ExtensionTestUtils.loadContentPage("http://example.com/dummy", {privateBrowsing: true});
-  await pb_extension.awaitFinish("webRequest.private");
-  await pb_extension.unload();
   await contentPage.close();
 
   contentPage = await ExtensionTestUtils.loadContentPage("http://example.com/dummy");
   await extension.awaitFinish("webRequest");
+  await pb_extension.awaitFinish("webRequest.spanning");
+  await contentPage.close();
+
+  await pb_extension.unload();
   await extension.unload();
-  await contentPage.close();
 
   Services.prefs.clearUserPref("extensions.allowPrivateBrowsingByDefault");
 });
--- a/toolkit/components/extensions/test/xpcshell/test_proxy_incognito.js
+++ b/toolkit/components/extensions/test/xpcshell/test_proxy_incognito.js
@@ -22,17 +22,17 @@ add_task(async function test_incognito_p
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       permissions: ["proxy", "<all_urls>"],
     },
     async background() {
       browser.proxy.onRequest.addListener(async (details) => {
         browser.test.assertFalse(details.incognito, "incognito flag is not set");
         browser.test.notifyPass("proxy.onRequest");
-      }, {urls: ["<all_urls>"]});
+      }, {urls: ["<all_urls>"], types: ["main_frame"]});
 
       // Actual call arguments do not matter here.
       await browser.test.assertRejects(
         browser.proxy.settings.set({value: {
           proxyType: "none",
         }}),
         /proxy.settings requires private browsing permission/,
         "proxy.settings requires private browsing permission.");
@@ -45,33 +45,39 @@ add_task(async function test_incognito_p
 
   let pextension = ExtensionTestUtils.loadExtension({
     incognitoOverride: "spanning",
     manifest: {
       permissions: ["proxy", "<all_urls>"],
     },
     background() {
       browser.proxy.onRequest.addListener(async (details) => {
-        browser.test.assertTrue(details.incognito, "incognito flag is set");
-        browser.test.notifyPass("proxy.onRequest.private");
-      }, {urls: ["<all_urls>"]});
+        browser.test.assertTrue(details.incognito, "incognito flag is set with filter");
+        browser.test.sendMessage("proxy.onRequest.private");
+      }, {urls: ["<all_urls>"], types: ["main_frame"], incognito: true});
+
+      browser.proxy.onRequest.addListener(async (details) => {
+        browser.test.assertFalse(details.incognito, "incognito flag is not set with filter");
+        browser.test.notifyPass("proxy.onRequest.spanning");
+      }, {urls: ["<all_urls>"], types: ["main_frame"], incognito: false});
     },
   });
   await pextension.startup();
 
   let contentPage = await ExtensionTestUtils.loadContentPage("http://example.com/dummy", {privateBrowsing: true});
-  await pextension.awaitFinish("proxy.onRequest.private");
-  await pextension.unload();
+  await pextension.awaitMessage("proxy.onRequest.private");
   await contentPage.close();
 
   contentPage = await ExtensionTestUtils.loadContentPage("http://example.com/dummy");
   await extension.awaitFinish("proxy.onRequest");
+  await pextension.awaitFinish("proxy.onRequest.spanning");
+  await contentPage.close();
 
+  await pextension.unload();
   await extension.unload();
-  await contentPage.close();
 
   Services.prefs.clearUserPref("extensions.allowPrivateBrowsingByDefault");
 });
 
 function scriptData(script) {
   return String(script).replace(/^.*?\{([^]*)\}$/, "$1");
 }
 
--- a/toolkit/components/extensions/webrequest/ChannelWrapper.cpp
+++ b/toolkit/components/extensions/webrequest/ChannelWrapper.cpp
@@ -516,23 +516,27 @@ bool ChannelWrapper::Matches(
     return false;
   }
 
   auto& urlInfo = FinalURLInfo();
   if (aFilter.mUrls && !aFilter.mUrls->Matches(urlInfo)) {
     return false;
   }
 
+  nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo();
+  bool isPrivate =
+      loadInfo && loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
+  if (!aFilter.mIncognito.IsNull() && aFilter.mIncognito.Value() != isPrivate) {
+    return false;
+  }
+
   if (aExtension) {
     // Verify extension access to private requests
-    if (!aExtension->PrivateBrowsingAllowed()) {
-      nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo();
-      if (loadInfo && loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0) {
-        return false;
-      }
+    if (isPrivate && !aExtension->PrivateBrowsingAllowed()) {
+      return false;
     }
 
     bool isProxy =
         aOptions.mIsProxy && aExtension->HasPermission(nsGkAtoms::proxy);
     // Proxies are allowed access to all urls, including restricted urls.
     if (!aExtension->CanAccessURI(urlInfo, false, !isProxy)) {
       return false;
     }
--- a/toolkit/modules/addons/WebRequest.jsm
+++ b/toolkit/modules/addons/WebRequest.jsm
@@ -26,17 +26,18 @@ function runLater(job) {
 }
 
 function parseFilter(filter) {
   if (!filter) {
     filter = {};
   }
 
   // FIXME: Support windowId filtering.
-  return {urls: filter.urls || null, types: filter.types || null};
+  return {urls: filter.urls || null, types: filter.types || null,
+          incognito: filter.incognito !== undefined ? filter.incognito : null};
 }
 
 function parseExtra(extra, allowed = [], optionsObj = {}) {
   if (extra) {
     for (let ex of extra) {
       if (!allowed.includes(ex)) {
         throw new ExtensionUtils.ExtensionError(`Invalid option ${ex}`);
       }