Bug 967895 - Prompt (w/ Site Permission) before allowing content to extract canvas data (Tor 6253). r=johannh
authorChung-Sheng Fu <cfu@mozilla.com>
Mon, 21 Aug 2017 16:52:42 +0800
changeset 386101 0c8403512a191485985a14707bf35c0a9060dfe6
parent 386100 6796e53fa9e386fd1709ddb2fd45eb74ab4e7b77
child 386102 96d4c5d83b4cb1e33b58007aee8216d40beaf99b
push id32675
push userarchaeopteryx@coole-files.de
push dateFri, 13 Oct 2017 21:36:21 +0000
treeherdermozilla-central@684b9ee0468e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjohannh
bugs967895
milestone58.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 967895 - Prompt (w/ Site Permission) before allowing content to extract canvas data (Tor 6253). r=johannh MozReview-Commit-ID: 91eeagGmUXv
browser/base/content/browser.js
browser/base/content/browser.xul
browser/locales/en-US/chrome/browser/browser.dtd
browser/locales/en-US/chrome/browser/browser.properties
browser/themes/shared/jar.inc.mn
browser/themes/shared/notification-icons.inc.css
browser/themes/shared/notification-icons/canvas.svg
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1459,16 +1459,17 @@ var gBrowserInit = {
     Services.obs.addObserver(gXPInstallObserver, "addon-install-origin-blocked");
     Services.obs.addObserver(gXPInstallObserver, "addon-install-failed");
     Services.obs.addObserver(gXPInstallObserver, "addon-install-confirmation");
     Services.obs.addObserver(gXPInstallObserver, "addon-install-complete");
     window.messageManager.addMessageListener("Browser:URIFixup", gKeywordURIFixup);
 
     BrowserOffline.init();
     IndexedDBPromptHelper.init();
+    CanvasPermissionPromptHelper.init();
 
     if (AppConstants.E10S_TESTING_ONLY)
       gRemoteTabsUI.init();
 
     // Initialize the full zoom setting.
     // We do this before the session restore service gets initialized so we can
     // apply full zoom settings to tabs restored by the session restore service.
     FullZoom.init();
@@ -1892,16 +1893,17 @@ var gBrowserInit = {
         Cu.reportError(ex);
       }
 
       if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) {
         MenuTouchModeObserver.uninit();
       }
       BrowserOffline.uninit();
       IndexedDBPromptHelper.uninit();
+      CanvasPermissionPromptHelper.uninit();
       PanelUI.uninit();
       AutoShowBookmarksToolbar.uninit();
     }
 
     // Final window teardown, do this last.
     window.XULBrowserWindow = null;
     window.QueryInterface(Ci.nsIInterfaceRequestor)
           .getInterface(Ci.nsIWebNavigation)
@@ -6676,16 +6678,92 @@ var IndexedDBPromptHelper = {
       browser, topic, message, this._notificationIcon, mainAction, secondaryActions,
       {
         persistent: true,
         hideClose: !Services.prefs.getBoolPref("privacy.permissionPrompts.showCloseButton"),
       });
   }
 };
 
+var CanvasPermissionPromptHelper = {
+  _permissionsPrompt: "canvas-permissions-prompt",
+  _notificationIcon: "canvas-notification-icon",
+
+  init() {
+    Services.obs.addObserver(this, this._permissionsPrompt);
+  },
+
+  uninit() {
+    Services.obs.removeObserver(this, this._permissionsPrompt);
+  },
+
+  // aSubject is an nsIBrowser (e10s) or an nsIDOMWindow (non-e10s).
+  // aData is an URL string.
+  observe(aSubject, aTopic, aData) {
+    if (aTopic != this._permissionsPrompt) {
+      return;
+    }
+
+    let browser;
+    if (aSubject instanceof Ci.nsIDOMWindow) {
+      let contentWindow = aSubject.QueryInterface(Ci.nsIDOMWindow);
+      browser = gBrowser.getBrowserForContentWindow(contentWindow);
+    } else {
+      browser = aSubject.QueryInterface(Ci.nsIBrowser);
+    }
+
+    let uri = Services.io.newURI(aData);
+    if (gBrowser.selectedBrowser !== browser) {
+      // Must belong to some other window.
+      return;
+    }
+
+    let message = gNavigatorBundle.getFormattedString("canvas.siteprompt", [ uri.asciiHost ]);
+
+    function setCanvasPermission(aURI, aPerm, aPersistent) {
+      Services.perms.add(aURI, "canvas/extractData", aPerm,
+                          aPersistent ? Ci.nsIPermissionManager.EXPIRE_NEVER
+                                      : Ci.nsIPermissionManager.EXPIRE_SESSION);
+    }
+
+    let mainAction = {
+      label: gNavigatorBundle.getString("canvas.allow"),
+      accessKey: gNavigatorBundle.getString("canvas.allow.accesskey"),
+      callback(state) {
+        setCanvasPermission(uri, Ci.nsIPermissionManager.ALLOW_ACTION,
+                            state && state.checkboxChecked);
+      }
+    };
+
+    let secondaryActions = [{
+      label: gNavigatorBundle.getString("canvas.notAllow"),
+      accessKey: gNavigatorBundle.getString("canvas.notAllow.accesskey"),
+      callback(state) {
+        setCanvasPermission(uri, Ci.nsIPermissionManager.DENY_ACTION,
+                            state && state.checkboxChecked);
+      }
+    }];
+
+    let checkbox = {
+      // In PB mode, we don't want the "always remember" checkbox
+      show: !PrivateBrowsingUtils.isWindowPrivate(window)
+    };
+    if (checkbox.show) {
+      checkbox.checked = true;
+      checkbox.label = gBrowserBundle.GetStringFromName("canvas.remember");
+    }
+
+    let options = {
+      checkbox
+    };
+    PopupNotifications.show(browser, aTopic, message, this._notificationIcon,
+                            mainAction, secondaryActions, options);
+  }
+};
+
 function CanCloseWindow() {
   // Avoid redundant calls to canClose from showing multiple
   // PermitUnload dialogs.
   if (Services.startup.shuttingDown || window.skipNextCanClose) {
     return true;
   }
 
   let timedOutProcesses = new WeakSet();
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -818,16 +818,18 @@
                      onmouseout="document.getElementById('identity-box').classList.remove('no-hover');"
                      align="center">
                   <image id="default-notification-icon" class="notification-anchor-icon" role="button"
                          tooltiptext="&urlbar.defaultNotificationAnchor.tooltip;"/>
                   <image id="geo-notification-icon" class="notification-anchor-icon geo-icon" role="button"
                          tooltiptext="&urlbar.geolocationNotificationAnchor.tooltip;"/>
                   <image id="addons-notification-icon" class="notification-anchor-icon install-icon" role="button"
                          tooltiptext="&urlbar.addonsNotificationAnchor.tooltip;"/>
+                  <image id="canvas-notification-icon" class="notification-anchor-icon" role="button"
+                         tooltiptext="&urlbar.canvasNotificationAnchor.tooltip;"/>
                   <image id="indexedDB-notification-icon" class="notification-anchor-icon indexedDB-icon" role="button"
                          tooltiptext="&urlbar.indexedDBNotificationAnchor.tooltip;"/>
                   <image id="password-notification-icon" class="notification-anchor-icon login-icon" role="button"
                          tooltiptext="&urlbar.passwordNotificationAnchor.tooltip;"/>
                   <stack id="plugins-notification-icon" class="notification-anchor-icon" role="button" align="center"
                          tooltiptext="&urlbar.pluginsNotificationAnchor.tooltip;">
                     <image class="plugin-icon" />
                     <image id="plugin-icon-badge" />
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -200,16 +200,17 @@ These should match what Safari and other
 <!ENTITY printButton.label            "Print">
 <!ENTITY printButton.tooltip          "Print this page">
 
 <!ENTITY urlbar.viewSiteInfo.label                      "View site information">
 
 <!ENTITY urlbar.defaultNotificationAnchor.tooltip         "Open message panel">
 <!ENTITY urlbar.geolocationNotificationAnchor.tooltip     "Open location request panel">
 <!ENTITY urlbar.addonsNotificationAnchor.tooltip          "Open add-on installation message panel">
+<!ENTITY urlbar.canvasNotificationAnchor.tooltip          "Manage canvas extraction permission">
 <!ENTITY urlbar.indexedDBNotificationAnchor.tooltip       "Open offline storage message panel">
 <!ENTITY urlbar.passwordNotificationAnchor.tooltip        "Open save password message panel">
 <!ENTITY urlbar.pluginsNotificationAnchor.tooltip         "Manage plug-in use">
 <!ENTITY urlbar.webNotificationAnchor.tooltip             "Change whether you can receive notifications from the site">
 <!ENTITY urlbar.persistentStorageNotificationAnchor.tooltip     "Store data in Persistent Storage">
 <!ENTITY urlbar.remoteControlNotificationAnchor.tooltip   "Browser is under remote control">
 
 <!ENTITY urlbar.webRTCShareDevicesNotificationAnchor.tooltip      "Manage sharing your camera and/or microphone with the site">
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -462,16 +462,25 @@ offlineApps.allowStoring.label=Allow Sto
 offlineApps.allowStoring.accesskey=A
 offlineApps.dontAllow.label=Don’t Allow
 offlineApps.dontAllow.accesskey=n
 
 offlineApps.usage=This website (%S) is now storing more than %SMB of data on your computer for offline use.
 offlineApps.manageUsage=Show settings
 offlineApps.manageUsageAccessKey=S
 
+# Canvas permission prompt
+# LOCALIZATION NOTE (canvas.siteprompt): %S is hostname
+canvas.siteprompt=Will you allow %S to use your HTML5 canvas image data? This may be used to uniquely identify your computer.
+canvas.notAllow=Don’t Allow
+canvas.notAllow.accesskey=n
+canvas.allow=Allow Data Access
+canvas.allow.accesskey=A
+canvas.remember=Always remember my decision
+
 identity.identified.verifier=Verified by: %S
 identity.identified.verified_by_you=You have added a security exception for this site.
 identity.identified.state_and_country=%S, %S
 
 identity.icon.tooltip=Show site information
 identity.extension.label=Extension (%S)
 identity.extension.tooltip=Loaded by extension: %S
 identity.showDetails.tooltip=Show connection details
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -56,16 +56,17 @@
   skin/classic/browser/identity-icon-notice.svg                (../shared/identity-block/identity-icon-notice.svg)
   skin/classic/browser/info.svg                                (../shared/info.svg)
   skin/classic/browser/searchReset.css                         (../shared/searchReset.css)
 
   skin/classic/browser/illustrations/error-session-restore.svg (../shared/illustrations/error-session-restore.svg)
 
   skin/classic/browser/notification-icons/camera-blocked.svg                (../shared/notification-icons/camera-blocked.svg)
   skin/classic/browser/notification-icons/camera.svg                        (../shared/notification-icons/camera.svg)
+  skin/classic/browser/notification-icons/canvas.svg                        (../shared/notification-icons/canvas.svg)
   skin/classic/browser/notification-icons/default-info.svg                  (../shared/notification-icons/default-info.svg)
   skin/classic/browser/notification-icons/desktop-notification-blocked.svg  (../shared/notification-icons/desktop-notification-blocked.svg)
   skin/classic/browser/notification-icons/desktop-notification.svg          (../shared/notification-icons/desktop-notification.svg)
   skin/classic/browser/notification-icons/focus-tab-by-prompt.svg           (../shared/notification-icons/focus-tab-by-prompt.svg)
   skin/classic/browser/notification-icons/indexedDB-blocked.svg             (../shared/notification-icons/indexedDB-blocked.svg)
   skin/classic/browser/notification-icons/indexedDB.svg                     (../shared/notification-icons/indexedDB.svg)
   skin/classic/browser/notification-icons/login-detailed.svg                (../shared/notification-icons/login-detailed.svg)
   skin/classic/browser/notification-icons/login.svg                         (../shared/notification-icons/login.svg)
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -112,16 +112,21 @@
 .screen-icon.in-use {
   list-style-image: url(chrome://browser/skin/notification-icons/screen.svg);
 }
 
 .screen-icon.blocked-permission-icon {
   list-style-image: url(chrome://browser/skin/notification-icons/screen-blocked.svg);
 }
 
+#canvas-notification-icon,
+.popup-notification-icon[popupid="canvas-permissions-prompt"] {
+  list-style-image: url(chrome://browser/skin/notification-icons/canvas.svg);
+}
+
 #webRTC-preview:not([hidden]) {
   display: -moz-stack;
   border-radius: 4px;
   border: 1px solid GrayText;
   overflow: hidden;
   min-width: 300px;
   min-height: 10em;
 }
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/canvas.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 481.156 508.687" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M477.656 289.656c-36.133 224.615-220.16 222.188-283 218C22.22 496.163-68.584 254.586 61.98 128.066l105.857 105.872c-7.183 24.6 2.7 54.1 42.418 70.59 42.865 17.8 87.4 9.747 87.4 9.747s-14.08-22.03-15.565-68.266c-1.1-34.2-40.15-55.996-72.513-50.483L108.757 94.69c30.6-12.34 59.033-1.8 69.9 6.966 26.87 21.688 16.616 68.436 53 54 70.87-28.12-40.744-132.32 53-154 79.026-18.278 220.516 116.945 193 288zm-371-14a41 41 0 1 0 41 41 41 41 0 0 0-41-41zm149.5 92a43.5 43.5 0 1 0 43.5 43.5 43.5 43.5 0 0 0-43.5-43.5zm97.5-273a40 40 0 1 0 40 40 40 40 0 0 0-40-40zm24.5 141a45.5 45.5 0 1 0 45.5 45.5 45.5 45.5 0 0 0-45.5-45.5z"/>
+  <path d="M213.656 296.656c-36.083-15.022-42.678-42.92-33.52-64.423L35.847 87.925a17.732 17.732 0 0 1 25.076-25.078L205.45 207.393c28.076-10.037 67.206 8.853 68.206 40.263 1.24 38.716 13 58 13 58s-37.2 5.905-73-9z"/>
+</svg>